diff -r 000000000000 -r 08ec8eefde2f persistentstorage/centralrepository/cenrepsrv/shrepos.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/centralrepository/cenrepsrv/shrepos.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,980 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "srvdefs.h" +#include "srvres.h" +#include "shrepos.h" +#include "srvrepos_noc.h" +#include "obsrvr_noc.h" +#include "srvparams.h" + +CSharedRepository* CSharedRepository::NewL(TUid aUid) + { + CSharedRepository* self = new(ELeave) CSharedRepository(); + CleanupStack::PushL(self); + self->ConstructL(aUid); + CleanupStack::Pop(self); + + // debug check that CRepository::TTransactionMode modes match those used internally + // from CRepositoryTransactor: internal state logic relies on this + // there should be a better location for these checks... + ASSERT(CRepository::EReadTransaction == static_cast(EReadTransaction)); + ASSERT(CRepository::EConcurrentReadWriteTransaction == static_cast(EConcurrentReadWriteTransaction)); + ASSERT(CRepository::EReadWriteTransaction == static_cast(EReadWriteTransaction)); + + return self; + } + +void CSharedRepository::ConstructL(TUid aUid) + { + iSimRep = CHeapRepository::NewL(aUid); + } + +CSharedRepository::CSharedRepository() : iNotificationState(ETrue) + { + } + +CSharedRepository::~CSharedRepository() + { + if (iSimRep) + { + delete iSimRep; + } + } + +TUid CSharedRepository::Uid() const + { + return iSimRep->Uid(); + } + +/** +Stores the repository in-memory content to the related repository file on drive C. +If the operation fails, the in-memory content won't match the content of +the repository file (which will be kept as it was before the CommitChangesL() call). +In order to keep the consistency, the in-memory repository content is deleted now +and restored later, on the next repository operation. +*/ +TInt CSharedRepository::CommitChanges(TCentRepLocation aLocation) + { + iInconsistentData=ETrue; + + HBufC* filePath(NULL); + TRAPD(err,TServerResources::CreateRepositoryFileNameL(filePath,iSimRep->Uid(),aLocation,ECre)); + if (err!=KErrNone) + { + iSimRep->ResetContent(); + return err; + } + + // should not be committing while transactions are still active + ASSERT(!IsTransactionActive()); + TInt ret=iSimRep->CommitChanges(TServerResources::iFs,TServerResources::iPersistsVersion,*filePath); + if (ret==KErrNone) + { + iInconsistentData=EFalse; + } + delete filePath; + return ret; + } + +// merge transaction settings (which may include entries flagged as deleted), persist and notify +// private method relies on calling code to ensure it is permitted to make changes here. +// if this method is committing any changes, it cancels all other sessions' transactions +TInt CSharedRepository::DoCommitTransactionSettings(CRepositoryTransactor& aTransactor, TUint32& aKeyInfo) + { + aKeyInfo = KUnspecifiedKey; + if (0 == aTransactor.iTransactionSettings.Count()) + { + aKeyInfo = 0; // == number of settings modified + return KErrNone; // nothing to do + } + TInt error = iSimRep->SettingsArray().MergeArray(aTransactor.iTransactionSettings, iSimRep->DeletedSettingsArray(), ETransactionMerge); + TInt numChanges = aTransactor.iTransactionSettings.Count(); + if (numChanges == 0) + { + if (error == KErrNone) + { + aKeyInfo = 0; // no changes + } + // no changes were made, so the internal cache is still valid. + // This could be because there were no changes: empty list, only deletes on + // non-existent items (a setting created and deleted in the transaction), + // or because of error, such as failure of an initial realloc failure. + return error; + } + if (error != KErrNone) + { + // the repository is corrupted. Dump it for lazy loading later + ResetContent(); + + // mark cache as inconsistent so it is reloaded. + iInconsistentData = ETrue; + return error; + } + if (error == KErrNone) + { + // changes have been made: fail all other sessions' transactions so we can commit + FailAllTransactions(/*aExcludeTransactor=*/&aTransactor); + error = CommitChanges(); // this already calls ResetContent() in case of failure + } + if (error == KErrNone) + { + // settings are now persistent on disk: we can now notify about the changes + // following will notify about objects that are created and deleted in the transaction + // this could be made faster by having a multiple Notify method. + // That would also allow Notify messages to be more descriptive - ranges of Keys + for (TInt i = 0; i < numChanges; i++) + { + Notify(aTransactor.iTransactionSettings[i].Key()); + } + aKeyInfo = /*reinterpret_cast*/numChanges; + } + return error; + } + +void CSharedRepository::SetMetaDataOnRead(TServerSetting& aSetting, TBool aSingleMetaFound) + { + iSimRep->SetMetaDataOnRead(aSetting, aSingleMetaFound); + } + +void CSharedRepository::SetMetaDataOnCreate(TServerSetting& aNewSetting, TUint32* aMeta) + { + if(aMeta) + { + aNewSetting.SetMeta(*aMeta); + } + else + { + // No metadata specified. First check for a matching "range" default + // metadata setting + TSettingsDefaultMeta* defaultMeta = iSimRep->RangeMetaArray().Find(aNewSetting.Key()); + if (defaultMeta) + { + aNewSetting.SetMeta(defaultMeta->GetDefaultMetadata()); + } + else + { + // Range value not found, try for a repository default + aNewSetting.SetMeta(iSimRep->DefaultMeta()); + } + } + } + +void CSharedRepository::CreateL(TServerSetting& aSetting, TSettingsAccessPolicy*& aPolicy, TBool aFirstLoad, TBool aSingleMetaFound) + { + User::LeaveIfError(iSimRep->Create(aSetting, aPolicy, aSingleMetaFound)); + if (!aFirstLoad) + { + Notify(aSetting.Key()); + } + } + +// deletes an individual setting in the shared repository and makes it persistent +// if changes are made, all sessions' transactions are failed +TInt CSharedRepository::DeleteAndPersist(TUint32 aId) + { + TServerSetting* s = iSimRep->SettingsArray().Find(aId); + if(!s) + return KErrNotFound; + iSimRep->SettingsArray().Remove(aId); + + // removed a setting, so must fail all sessions' transactions before commit possible + FailAllTransactions(/*aExcludeTransactor=*/NULL); + TInt error = CommitChanges(); + if (error == KErrNone) + { + Notify(aId); + } + return error; + } + +// deletes an individual setting without making it persistent +// must not be called while any sessions are in transactions +TInt CSharedRepository::DeleteNoPersist(TUint32 aId) + { + // should only be calling this if no transactions are active + ASSERT(!IsTransactionActive()); + TServerSetting* s = iSimRep->SettingsArray().Find(aId); + if(!s) + return KErrNotFound; + + iSimRep->SettingsArray().Remove(aId); + return KErrNone; + } + +TInt CSharedRepository::ResetNoPersistL(TServerSetting& aSetting) + { + TServerSetting* s = iSimRep->SettingsArray().Find(aSetting.Key()); + if ((!s) || (*s != aSetting)) + { + if (s) + { + // save access policy of setting + TSettingsAccessPolicy* policy=s->AccessPolicy(); + s->Transfer(aSetting); + // restore access policy of setting + s->SetAccessPolicy(policy); + } + else + { + TServerSetting setting; + setting.Transfer(aSetting); + setting.SetAccessPolicy(GetFallbackAccessPolicy(setting.Key())); + setting.PushL(); + iSimRep->SettingsArray().OrderedInsertL(setting); + + TInt index = iSimRep->DeletedSettingsArray().FindInUnsignedKeyOrder(aSetting.Key()); + if (index != KErrNotFound) + iSimRep->DeletedSettingsArray().Remove(index); + + setting.Pop(); + } + } + else + { + return KErrGeneral; + } + return KErrNone; + } + +// if changes are made, all sessions' transactions are failed +void CSharedRepository::ResetAndPersistL(TServerSetting& aSetting) + { + if (ResetNoPersistL(aSetting) == KErrNone) + { + // changed a setting, so must fail all sessions' transactions + // before commit possible + FailAllTransactions(/*aExcludeTransactor=*/NULL); + CommitChangesL(); + Notify(aSetting.Key()); + } + } + +TInt CSharedRepository::ResetAllNoPersistL(CSharedRepository& aNewContent) + { + // mark cache as inconsistent in case Reset fails, so it is reloaded. + iInconsistentData=ETrue; + + // should not change repository while transactions in progress: should fail them first + ASSERT(!IsTransactionActive()); + TInt newCount = (aNewContent.iSimRep)->SettingsArray().Count(); + TInt count = iSimRep->SettingsArray().Count(); + + TInt newIndex = 0; + TInt index = 0; + + while(newIndexSettingsArray()[newIndex]; + const TServerSetting& setting = iSimRep->SettingsArray()[index]; + + TUint32 newKey = newSetting.Key(); + TUint32 key = setting.Key(); + + if(newKeykey) + { + Notify(key); + index++; + } + } + + while(newIndexSettingsArray()[newIndex++].Key()); + } + + while(indexSettingsArray()[index++].Key()); + } + + // Replace current settings with settings read from ROM, this + // will leave settings pointing to new single policies + iSimRep->SettingsArray().AdoptL((aNewContent.iSimRep)->SettingsArray()); + + // Reset policy pointers to point at this repositories policies + newCount=iSimRep->SettingsArray().Count(); + for(TInt i=0; iSettingsArray())[i].SetAccessPolicy(NULL); + TUint32 key = (iSimRep->SettingsArray())[i].Key(); + (iSimRep->SettingsArray())[i].SetAccessPolicy(GetFallbackAccessPolicy(key)); + } + + iSimRep->DeletedSettingsArray().Reset(); + + iInconsistentData=EFalse; + return KErrNone; + } + +// returns the read security policy used if there is no per-setting policy at aId +const TSecurityPolicy& CSharedRepository::GetFallbackReadAccessPolicy(TUint32 aId) + { + return iSimRep->GetFallbackReadAccessPolicy(aId); + } + +// returns the write security policy used if there is no per-setting policy at aId +const TSecurityPolicy& CSharedRepository::GetFallbackWriteAccessPolicy(TUint32 aId) + { + return iSimRep->GetFallbackWriteAccessPolicy(aId); + } + +// Get pointer to security policy that applies to a given setting +TSettingsAccessPolicy* CSharedRepository::GetFallbackAccessPolicy(TUint32 aId +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + ,TBool aSkipSingle +#endif + ) + { + return iSimRep->GetFallbackAccessPolicy(aId +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + ,aSkipSingle +#endif + ); + } + + +TInt CSharedRepository::ReadSettingSavePolicyL(CIniFileIn& aFile,TServerSetting& aSetting, TSettingsAccessPolicy*& aPolicy, TBool& aSingleMetaFound) + { + return iSimRep->ReadSettingSavePolicyL(aFile, aSetting, aPolicy, aSingleMetaFound); + } + +// Merge settings in this->iSettings with the iSettings of aMergeRep +// During an intsall/upgrade event aMergeRep will be created from the installed file +// During an upinstall event aMergeRep will be created from the ROM file +void CSharedRepository::MergeL(CSharedRepository& aMergeRep, TMergeType aMergeType) + { + // Process settings from main section - this updates values only + User::LeaveIfError(GetSettings().MergeArray(aMergeRep.GetSettings(), iSimRep->DeletedSettingsArray(), aMergeType)); + + //if the merging is due to a ROM Flash, we need to copy over both the NEW ROM keypsace global properties + //(default access policies/metadata and range policies/metadata), individual policies, we then need to ensure + //that the settings point at the correct individual policies and metadata. + if (aMergeType==ERomFlash) + { + //copy the default/range/individual policy + iSimRep->SetDefaultPolicy(aMergeRep.iSimRep->GetDefaultAccessPolicy()); +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + iSimRep->GetDefaultAccessPolicy().iHighKey=aMergeRep.iSimRep->GetDefaultAccessPolicy().iHighKey; + iSimRep->GetDefaultAccessPolicy().iKeyMask=aMergeRep.iSimRep->GetDefaultAccessPolicy().iKeyMask; +#endif + iSimRep->RangePolicyArray().Reset(); + TInt count=aMergeRep.iSimRep->RangePolicyArray().Count(); + iSimRep->RangePolicyArray().ReserveL(count); + for (TInt i=0;iRangePolicyArray().AppendL(aMergeRep.iSimRep->RangePolicyArray()[i]); + } + iSimRep->SinglePolicyArray().ResetAndDestroy(); + count=aMergeRep.iSimRep->SinglePolicyArray().Count(); + iSimRep->SinglePolicyArray().ReserveL(count); + for (TInt i=0;iSinglePolicyArray().AppendL(aMergeRep.iSimRep->SinglePolicyArray()[i]); + } + //now need to reset the aMergeRep single policies so it is not going to destroy the + //individual policies as ownership has been transferred + aMergeRep.iSimRep->SinglePolicyArray().Reset(); + + //copy the default/range metadata + iSimRep->SetDefaultMeta(aMergeRep.iSimRep->DefaultMeta()); + iSimRep->RangeMetaArray().Reset(); + count=aMergeRep.iSimRep->RangeMetaArray().Count(); + iSimRep->RangeMetaArray().ReserveL(count); + for (TInt i=0;iRangeMetaArray().AppendL(aMergeRep.iSimRep->RangeMetaArray()[i]); + } + + //set the timestamp,owner etc + iSimRep->SetTimeStamp(aMergeRep.iSimRep->TimeStamp()); + iSimRep->SetOwner(aMergeRep.iSimRep->Owner()); + } + + // Update all access policies and meta + for(TInt i=0; iSettingsArray().Count();i++) + { + TServerSetting& setting= iSimRep->SettingsArray()[i]; + setting.SetAccessPolicy(GetFallbackAccessPolicy(setting.Key())); + } + } + +// Save timestamp of installed file +void CSharedRepository::SetInstallTime(TTime aInstallTime) + { + iSimRep->SetTimeStamp(aInstallTime); + } + +// Handle creation or upgrade of file in install directory +void CSharedRepository::HandleUpdateMergeL(TTime aInstallFileTimeStamp, CSharedRepository& aInstallRep) + { + MergeL(aInstallRep, ESWIUpgradeMerge); + + SetInstallTime(aInstallFileTimeStamp); // Set merge timestamp + CommitChangesL(); // Commit changes to write system drive file + + // settings are now persistent on disk: we can now notify about the changes + for (TInt i = 0; i < (aInstallRep.iSimRep)->SettingsArray().Count(); i++) + { + Notify((aInstallRep.iSimRep)->SettingsArray()[i].Key()); + } + } + +// Handle merge activity due to an uninstall +void CSharedRepository::HandleDeleteMergeL(CSharedRepository& aRomRep) + { + MergeL(aRomRep, ESWIDowngradeMerge); + + SetInstallTime(0); // Reset timestamp + CommitChangesL(); // Commit changes to write system drive file + + // settings are now persistent on disk: we can now notify about the changes + for (TInt i = 0; i < (aRomRep.iSimRep)->SettingsArray().Count(); i++) + { + Notify((aRomRep.iSimRep)->SettingsArray()[i].Key()); + } + } + + +#ifdef CENTREP_CONV_TOOL +/** +Statement "iInconsistentData = ETrue;" must be the first statement in the method, +"iInconsistentData = EFalse;" must be the last. It is used for lasy-load implementation +for the repository and solves the problem that if CommitChangesL() fails the in-memory +repository data won't match the repository data, stored in the file. +This routine is being retained for testing purposes +*/ +void CSharedRepository::DoCommitChangesToIniFileL(const TDesC& aOutFileName +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + ,TUint32 aCreVersion +#endif + ) + { + iInconsistentData=ETrue; + + // should not be committing while transactions are still active + ASSERT(!IsTransactionActive()); + + iSimRep->DoCommitChangesToIniFileL(TServerResources::iFs,aOutFileName +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + ,aCreVersion +#endif + ); + + iInconsistentData = EFalse; + } +#endif //CENTREP_CONV_TOOL + +/** +The method reloads the repository content from a repository file. +The current repository must be emptied (or must be empty already) before the call is made. +@param aIniFile A reference to CIniFileIn object, which will be used to load + the repository content. +@return KErrCorrupt Corrupted repository file. + KErrNone The repository content was seccessfully loaded into memory. + KErrNotFound Setting not found in the file. +@leave System-wide error codes. +@leave KErrGeneral It's probably a programmer's error - current CSharedRepository + object is partially initialised. +*/ +TInt CSharedRepository::ReloadContentL(CIniFileIn& aIniFile, TBool aFirstLoad) + { + // Preconditions - CHeapRepository object should be an empty one. + if(!iSimRep->IsEmpty()) + { + User::Leave(KErrGeneral); + } + TInt err = iSimRep->ReloadContentExceptSettingsL(aIniFile); + if(err == KErrCorrupt) + { + return err; + } + CleanupClosePushL(iSimRep->RangeMetaArray()); + CleanupClosePushL(iSimRep->RangePolicyArray()); + + // Settings + TServerSetting setting; + TSettingsAccessPolicy* policy; + TBool singleMetaFound; + TCleanupItem tc(CHeapRepository::SinglePoliciesCleanup, &(iSimRep->SinglePolicyArray())); + CleanupStack::PushL(tc); + CleanupClosePushL(iSimRep->SettingsArray()); + while((err = ReadSettingSavePolicyL(aIniFile, setting, policy, singleMetaFound)) == KErrNone) + { + setting.PushL(); + if(iSimRep->SettingsArray().IsDefault()) + { + setting.SetClean(); + } + CreateL(setting, policy, aFirstLoad, singleMetaFound); + setting.Pop(); + } + if(err == KErrNotFound) + { + err = KErrNone; + } + if (err == KErrNone) + { + CleanupStack::Pop(4,&(iSimRep->RangeMetaArray())); + } + else + { + CleanupStack::PopAndDestroy(4,&(iSimRep->RangeMetaArray())); + } + return err; + } + +/** +Resets current repository data - actually all of them, which may be loaded from +the related ini file. +The iUid data member value is kept as it was at the moment of creation of +CSharedRepository object. +*/ +void CSharedRepository::ResetContent() + { + iSimRep->ResetContent(); + } + +/** +This function is used to restore the notification, which was temporary disabled +when making RestoreConsistencyL() call. +@param aNotificationState It points to CObservable::iNotificationState data member, which + controls the notification state - active or disabled. +@internalComponent +*/ +static void RestoreNotification(void* aNotificationState) + { + TBool* notificationState = static_cast (aNotificationState); + *notificationState = ETrue; + } + +/** +The method reloads the repository content from the related ini file if previous +CommitChangesL() has not completed successfully. +*/ +void CSharedRepository::RestoreConsistencyL() + { + //Do nothing if previous CommitChangesL() completed successfully. + if (!iInconsistentData) + { + return; + } + //Reset current repository data + ResetContent(); + //Disable notifications + TCleanupItem restoreNotification(&RestoreNotification, &iNotificationState); + CleanupStack::PushL(restoreNotification); + iNotificationState = EFalse; + //Reload the repository content from the related ini file + DoRestoreConsistencyL(); + //Activate notifications + CleanupStack::PopAndDestroy();//restoreNotification + + TCentRepLocation location = EPersists; + HBufC* persistsTmpFilePath(NULL); + //allocates memory on the heap + TServerResources::CreateRepositoryFileNameLC(persistsTmpFilePath,iSimRep->Uid(),location,ETmp); + // Remove any .tmp file + // If a debug build - record error + TInt fileDeleteErr=TServerResources::iFs.Delete(*persistsTmpFilePath); + if ((fileDeleteErr != KErrNone) && (fileDeleteErr != KErrNotFound)) + { + #ifdef _DEBUG + RDebug::Print(_L("CHeapRepository::RestoreConsistencyL - Failed to delete file. Error = %d"), fileDeleteErr); + #endif + } + + CleanupStack::PopAndDestroy(persistsTmpFilePath); + + iInconsistentData=EFalse; + } + +/** +The method reloads the repository content from the related cre or ini file. +@leave System-wide error codes +*/ +void CSharedRepository::DoRestoreConsistencyL() + { +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS + //note that the function below already handles the deletion of any corrupt file + //in non-rom location + TInt ret=TServerResources::iObserver->CreateRepositoryL(this,EPersists); + if (ret==KErrNotFound) + { + ret=TServerResources::iObserver->CreateRepositoryL(this,EInstall); + if (ret==KErrNotFound) + { + ret=TServerResources::iObserver->CreateRepositoryL(this,ERom); + User::LeaveIfError(ret); + } + } +#else + TCentRepLocation location; + + TInt err = FindLocationForFileL(location,iSimRep->Uid(),ECre); + if (err != KErrNotFound) + { + User::LeaveIfError(CreateRepositoryFromCreFileL(location)); + return; + } + + User::LeaveIfError(FindLocationForFileL(location,iSimRep->Uid(),EIni)); + + HBufC* fileName(NULL); + TServerResources::CreateRepositoryFileNameLC(fileName,iSimRep->Uid(),location,EIni); + + CIniFileIn* iniFile = NULL; + err = CIniFileIn::NewLC(TServerResources::iFs,iniFile,*fileName); + if (err==KErrCorrupt && location!=ERom) + { + User::LeaveIfError(TServerResources::iFs.Delete(*fileName)); + } + User::LeaveIfError(err); + + + err = ReloadContentL(*iniFile); + User::LeaveIfError(err); + + CleanupStack::PopAndDestroy(iniFile); //iniFile + CleanupStack::PopAndDestroy(fileName); //fileName +#endif + } + + +/** +This method looks for and sets a location for a given repository. +It is based on EAuto mode thus it goes through all locations in the +same order (EPersists - EInstall - ERom) + +@param aLocation - returns a location for a repository +@param aUid - id of a repository which location should be found +@param aType - repository file type (.txt or .cre) +@return KErrNone if aLocation succesfully set for a given repository, + KErrNotFound if a repository was not found in any locations. + +@internalTechnology +*/ +#ifndef SYMBIAN_CENTREP_SUPPORT_MULTIROFS +TInt CSharedRepository::FindLocationForFileL(TCentRepLocation& aLocation,TUid aUid,const TCentRepFileType aType) const + { + if(TServerResources::CentrepFileExistsL(aUid, EPersists, aType)) + { + aLocation = EPersists; + return KErrNone; + } + + if(TServerResources::CentrepFileExistsL(aUid, EInstall, aType)) + { + aLocation = EInstall; + return KErrNone; + } + + if(TServerResources::CentrepFileExistsL(aUid, ERom, aType)) + { + aLocation = ERom; + return KErrNone; + } + + return KErrNotFound; + } +#endif + +TInt CSharedRepository::CreateRepositoryFromCreFileL( TCentRepLocation aLocation) + { + // Get file path name from location + HBufC* filePath(NULL); + TServerResources::CreateRepositoryFileNameLC(filePath,iSimRep->Uid(), aLocation,ECre); + // Trap errors from repository creation so we can delete corrupt repositories + TRAPD(error, iSimRep->CreateRepositoryFromCreFileL(TServerResources::iFs,*filePath)); + if(error!=KErrNone && error!=KErrNotFound && error!=KErrNoMemory) + { + error=KErrCorrupt; + // store wasn't quite what we were expecting - can't return an error, can't leave + // so all we can do is close the file, tidy up as best we can, and return corrupt + if (aLocation != ERom) + { + // If a debug build - record error + TInt fileDeleteErr=TServerResources::iFs.Delete(*filePath); + if (fileDeleteErr != KErrNone) + { + #ifdef _DEBUG + RDebug::Print(_L("CSharedRepository::CreateRepositoryFromCreFileL - Failed to delete file. Error = %d"), fileDeleteErr); + #endif + } + + } + } + else if( error==KErrNoMemory) + { + User::Leave(KErrNoMemory); + } + CleanupStack::PopAndDestroy(filePath); + return error; + } + +/** Attempts to start a transaction. +Guaranteed to succeed (return KErrNone) for EConcurrentReadWriteTransaction mode only. +@param aTransactor transactor attempting to start transaction +@param aMode type of transaction to be started +@pre transactor is not in a transaction +@return KErrNone if the transaction is started, KErrLocked if read/write locks prevented that +type of transaction from starting now, and KErrArgument for invalid aMode. +@post On returning KErrNone, transaction is started and read/write locks are obtained for it +in the shared repository. Any other return: transaction has not started. +*/ +TInt CSharedRepository::StartTransaction(CRepositoryTransactor& aTransactor, TInt aMode) + { + // session can only be in one transaction + ASSERT(!aTransactor.IsInTransaction()); + + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + switch (aMode) + { + case EConcurrentReadWriteTransaction: + // can always start this type of transaction + shrepinfo->iNumActiveConcurrentReadWriteTransactions++; + break; + case EReadTransaction: + // negative lock means there is an active EReadWriteTransaction + if (shrepinfo->iPessimisticTransactionLockCount < 0) + { + ASSERT(shrepinfo->iPessimisticTransactionLockCount == -1); // sanity check + return KErrLocked; + } + // when non-negative lock equals number of active EReadTransactions. + shrepinfo->iPessimisticTransactionLockCount++; + break; + case EReadWriteTransaction: + // lock is zero if there are no active pessimistic transactions + if (shrepinfo->iPessimisticTransactionLockCount != 0) + { + return KErrLocked; + } + // lock value of -1 means the exclusive EReadWriteTransaction is active + shrepinfo->iPessimisticTransactionLockCount = -1; + break; + default: + // not a valid transaction mode + return KErrArgument; + } + aTransactor.AddToQueue(shrepinfo->iTransactors, aMode); + return KErrNone; + } + +/** Commit transaction +@return KErrNone on success, or error code. +@param aKeyInfo + on success (return KErrNone): aKeyInfo returns number of modified settings; + on failure (other error code): KUnspecifiedKey +@pre transactor is in a transaction. +@post transactor is not in a transaction +*/ +TInt CSharedRepository::CommitTransaction(CRepositoryTransactor& aTransactor, TUint32& aKeyInfo) + { + // calling code should have panicked the client if not in a transaction + ASSERT(aTransactor.IsInTransaction()); + TInt result = aTransactor.iTransactionResult; + if (aTransactor.IsInFailedTransaction()) + { + ASSERT(result != KErrNone); + aKeyInfo = aTransactor.iTransactionErrorKey; + } + else + { + ASSERT(result == KErrNone); + ASSERT(aTransactor.iTransactionErrorKey == KUnspecifiedKey); + aKeyInfo = 0; + // must release locks otherwise shared repository will not commit changes + // failed transactions have already released their locks + ReleaseTransactionLock(aTransactor); + } + + // transactions that haven't made any changes can be closed at any time + if (aTransactor.IsInActiveReadWriteTransaction() && + (aTransactor.iTransactionSettings.Count() > 0)) + { + result = DoCommitTransactionSettings(aTransactor, aKeyInfo); + } + + // transaction is complete - remove from queue + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + shrepinfo->iTransactors.Remove(aTransactor); + //Remove the link to the next transaction + aTransactor.iLink.iNext = NULL; + aTransactor.Deque(); + + return result; + } + +/** Cancels the transaction, discarding changes. +@post Not in a transaction +*/ +void CSharedRepository::CancelTransaction(CRepositoryTransactor& aTransactor) + { + if (aTransactor.IsInTransaction()) + { + ReleaseTransactionLock(aTransactor); + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + shrepinfo->iTransactors.Remove(aTransactor); + //Remove the link to the next transaction + aTransactor.iLink.iNext = NULL; + aTransactor.Deque(); + } + } + +TInt CSharedRepository::FailTransaction(CRepositoryTransactor& aTransactor, TInt aError, TUint32 aErrorKey) + { + ASSERT(aError != KErrNone); // must fail for a reason + if (aTransactor.IsInActiveTransaction()) + { + // locks cannot be removed from a failed transaction, so release before failing + ReleaseTransactionLock(aTransactor); + aTransactor.SetFailed(aError, aErrorKey); + } + return aError; // to allow "return FailTransaction(error, errorKey);" - error written once + } + +/** Fails all active transactions - except for the optional aExcludeTransactor, releasing locks. +All transactions are failed with reason "KErrLocked" meaning they are "locked out". +This should only be done to allow another agent to change values in the repository. +Beware that all concurrent read/write transactions that are failed with KErrLocked are +expected to retry the transactions straight afterwards - must be careful to allow their +retry strategy to be successful. +*/ +void CSharedRepository::FailAllTransactions(const CRepositoryTransactor* aExcludeTransactor) + { + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + TSglQueIter transIter(shrepinfo->iTransactors); + CRepositoryTransactor* transactor; + while ((transactor = transIter++) != NULL) + { + if (transactor != aExcludeTransactor) + { + FailTransaction(*transactor, KErrLocked, KUnspecifiedKey); + } + } + } + +/** must currently be in active Read transaction. Does not fail +transaction here if promotion to read/write failed. +@return KErrNone if promoted, KErrLocked if not +*/ +TInt CSharedRepository::AttemptPromoteTransactionToReadWrite(CRepositoryTransactor& aTransactor) + { + // transactor should currently be in an active read transaction + ASSERT(aTransactor.IsInActiveReadTransaction()); + + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + // sanity check: must only be pessimistic reads active + ASSERT(shrepinfo->iPessimisticTransactionLockCount > 0); + // can promote only if there are no other active read transactions: + if (1 == shrepinfo->iPessimisticTransactionLockCount) + { + // may only promote to exclusive read/write as it has the same commit semantics + // as Read transaction: concurrent R/W must wait for reads to finish first. + aTransactor.PromoteToExclusiveReadWrite(); + // lock value of -1 means the exclusive EReadWriteTransaction is active + shrepinfo->iPessimisticTransactionLockCount = -1; + return KErrNone; + } + return KErrLocked; + } + +/** Private helper method which releases any read/write locks held in the shared repository +by this transactor. Caller must set transactor's state or remove from queue as appropriate. +@param aTransactor transactor whose read/write locks are to be released. +@post Any read/write locks held by transactor are released. +*/ +void CSharedRepository::ReleaseTransactionLock(CRepositoryTransactor& aTransactor) + { + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + ASSERT(shrepinfo); + if (aTransactor.IsInActiveConcurrentReadWriteTransaction()) + { + shrepinfo->iNumActiveConcurrentReadWriteTransactions--; + ASSERT(shrepinfo->iNumActiveConcurrentReadWriteTransactions >= 0); // sanity check + } + else if (aTransactor.IsInActiveReadTransaction()) + { + shrepinfo->iPessimisticTransactionLockCount--; + ASSERT(shrepinfo->iPessimisticTransactionLockCount >= 0); // sanity check + } + else if (aTransactor.IsInActiveExclusiveReadWriteTransaction()) + { + // can only be one exclusive read/write transaction active (lock value -1) + ASSERT(shrepinfo->iPessimisticTransactionLockCount == -1); + shrepinfo->iPessimisticTransactionLockCount = 0; + } + } + +void CSharedRepository::ExternalizeCre(RWriteStream& aStream) const + { + iSimRep->ExternalizeCre(TServerResources::iPersistsVersion,aStream); + } + +void CSharedRepository::InternalizeCreL(RReadStream& aStream) + { + iSimRep->InternalizeCreL(aStream); + } + +#ifdef SYMBIAN_CENTREP_SUPPORT_MULTIROFS +void CSharedRepository::InternalizeCreL(RReadStream& aStream,TUint8& aCreVersion) + { + iSimRep->InternalizeCreL(aStream,aCreVersion); + } +#endif + +void CSharedRepository::Notify(TUint32 aVal) const + { + if(iNotificationState) + { + TServerResources::iObserver->Notify(iSimRep->Uid(), aVal); + } + } + +TBool CSharedRepository::IsTransactionActive() + { + CObservable::TSharedRepositoryInfo* shrepinfo = TServerResources::iObserver->SharedRepositoryInfo(iSimRep->Uid()); + if (shrepinfo) + { + return (shrepinfo->iPessimisticTransactionLockCount != 0) || + (shrepinfo->iNumActiveConcurrentReadWriteTransactions > 0); + } + return EFalse; + } + +RSettingsArray& CSharedRepository::GetSettings() + { + return iSimRep->SettingsArray(); + }