diff -r 000000000000 -r ba25891c3a9e ncdengine/provider/server/src/ncdnodecachecleaner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ncdengine/provider/server/src/ncdnodecachecleaner.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,2694 @@ +/* +* Copyright (c) 2006 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: Implements CNcdNodeCacheCleaner class +* +*/ + + +#include "ncdnodecachecleaner.h" + +#include "ncdnodeidentifierutils.h" +#include "ncdnodemanager.h" +#include "ncdnodedbmanager.h" +#include "ncdnodefactory.h" +#include "ncdnodeidentifiereditor.h" +#include "ncdnodeimpl.h" +#include "ncdnodefolder.h" +#include "ncdrootnode.h" +#include "ncdnodemetadataimpl.h" +#include "ncdnodelink.h" +#include "ncdnodeiconimpl.h" +#include "ncdnodescreenshotimpl.h" +#include "ncdnodeidentifier.h" +#include "ncdchildentity.h" +#include "catalogsconstants.h" +#include "ncdproviderdefines.h" +#include "catalogsutils.h" +#include "ncdgeneralmanager.h" + +#include "catalogsdebug.h" + + + +// Uncomment this if the node cache cleaner should be turned off. +// For example, when debugging other functionalities. +//#define NCD_NODE_CACHE_CLEANER_TURN_OFF + + +#ifdef CATALOGS_BUILD_CONFIG_DEBUG +// Uncomment this if the array prints should be printed when the +// debug mode is on. This is set optional because the array print +// may take a long time when data is written into the log file. +//#define NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT +#endif + + +// These priority values are used for this active object. +// The default value is used when normal cleaning is done +const CActive::TPriority KDefaultCleaningPriority( CActive::EPriorityIdle ); +// This priority value is used if the database size has been exceeded +// and the cleaning should be done with the higher priority +const CActive::TPriority KIncreasedCleaningPriority( CActive::EPriorityLow ); +// This priority value is used if the database size has been exceeded +// too much and the cleaning should be done with really high priority. +// This will slow down the other actions even more because cleaning +// takes more time. +const CActive::TPriority KMaxCleaningPriority( CActive::EPriorityStandard ); + +// The max db size value is divided by this number to get the +// value for the limit until which the db is freed after max size +// has been reached and cleaning started. +const TInt KMaxSizeDivider( 2 ); + +// If the db size is over double the accepted db size, then the max cleaning +// priority should be used. Because most likely the lower priority does not work +// efficiently enough +const TInt KMaxDbSizeRatio( 2 ); + +// The checking of the database should be done at least in some time intervals +// even if the db size has not been exceeded. +const TTimeIntervalMinutes KCheckPeriod( 60 ); + + + +CNcdNodeCacheCleaner* CNcdNodeCacheCleaner::NewL( CNcdGeneralManager& aGeneralManager, + CNcdNodeDbManager& aNodeDbManager, + TInt aDbDefaultMaxSize, + CNcdNodeFactory& aNodeFactory ) + { + CNcdNodeCacheCleaner* self = + CNcdNodeCacheCleaner::NewLC( aGeneralManager, + aNodeDbManager, + aDbDefaultMaxSize, + aNodeFactory ); + CleanupStack::Pop( self ); + return self; + } + +CNcdNodeCacheCleaner* CNcdNodeCacheCleaner::NewLC( CNcdGeneralManager& aGeneralManager, + CNcdNodeDbManager& aNodeDbManager, + TInt aDbDefaultMaxSize, + CNcdNodeFactory& aNodeFactory ) + { + CNcdNodeCacheCleaner* self = + new( ELeave ) CNcdNodeCacheCleaner( aGeneralManager, + aNodeDbManager, + aDbDefaultMaxSize, + aNodeFactory ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + + + +CNcdNodeCacheCleaner::CNcdNodeCacheCleaner( CNcdGeneralManager& aGeneralManager, + CNcdNodeDbManager& aNodeDbManager, + TInt aDbDefaultMaxSize, + CNcdNodeFactory& aNodeFactory ) +: CActive( KDefaultCleaningPriority ), + iGeneralManager( aGeneralManager ), + iNodeManager( aGeneralManager.NodeManager() ), + iNodeDbManager( aNodeDbManager ), + iDbMaxSize( aDbDefaultMaxSize ), + iNodeFactory( aNodeFactory ), + // EFalse by default so that we don't clean anything even in error situations + // for clients that don't allow it + iAllowCleaning( EFalse ), + iIncreasePriority( KDefaultCleaningPriority ), + iLastCleanupTime( 0 ) + { + } + + +void CNcdNodeCacheCleaner::ConstructL() + { + // These values have to be set. + + // These types will be removed from the db. + + // iNodeCleanupTypes are used to search the node items from db. + // This array is only for node type. So, it contains only one value. + iNodeCleanupTypes.AppendL( NcdNodeClassIds::ENcdNode ); + + // Meta cleanup types are also removed from the db. + // These types use the metadata identifier to remove the items + // from db + iMetaCleanupTypes.AppendL( NcdNodeClassIds::ENcdMetaData ); + + // These types should be permanent info + // or not to be removed by this cleaner for some other reason. + // Uncomment these if specifications change. + //iMetaCleanupTypes.Append( NcdNodeClassIds::ENcdNodeUserData ); + //iMetaCleanupTypes.Append( NcdNodeClassIds::ENcdSubscriptionsData ); + + + // Special cleanup types are removed from the db. + // But node or metadata ids cannot be directly used for these. + // They require special handling to get the right ids. + iIconCleanupTypes.AppendL( NcdNodeClassIds::ENcdIconData ); + + iScreenshotCleanupTypes.AppendL( NcdNodeClassIds::ENcdScreenshotData ); + + + // These namespaces should not be cleaned by this cleaner + iDoNotCleanNameSpaces = new(ELeave) CPtrCArray( KListGranularity ); + iDoNotCleanNameSpaces->AppendL( + NcdProviderDefines::KDownloadNamespace() ); + iDoNotCleanNameSpaces->AppendL( + NcdProviderDefines::KSubscriptionNamespace() ); + iDoNotCleanNameSpaces->AppendL( + NcdProviderDefines::KPreviewStorageNamespace() ); + + // Because this is an active object, we need to inform + // scheduler about it. + CActiveScheduler::Add( this ); + } + + +CNcdNodeCacheCleaner::~CNcdNodeCacheCleaner() + { + DLTRACEIN(("")); + + // It is always safe to call Cancel for active object + // when destructor is called. + Cancel(); + + // This object is automatically removed from the active scheduler when + // this object is deleted. + + // Delete member variables that are owned by this class object here. + + iNodeCleanupTypes.Reset(); + iMetaCleanupTypes.Reset(); + iIconCleanupTypes.Reset(); + + iScreenshotCleanupTypes.Reset(); + iCleanupIdentifiers.ResetAndDestroy(); + iAllDbStorageNodeItems.ResetAndDestroy(); + iAllDbIconItems.ResetAndDestroy(); + + iAllDbScreenshotItems.ResetAndDestroy(); + iDbMetaIdentifiers.ResetAndDestroy(); + iWaitingIdentifiers.ResetAndDestroy(); + iWaitingParentIdentifiers.ResetAndDestroy(); + iDoNotRemoves.ResetAndDestroy(); + iRootChildren.ResetAndDestroy(); + iBundleChildren.ResetAndDestroy(); + + // Notice that this delete also deletes the objects owned by the array. + delete iDoNotCleanNameSpaces; + + DLTRACEOUT(("")); + } + + +TInt CNcdNodeCacheCleaner::NodeIdentifierArraySortById( const CNcdNodeIdentifier& aNodeId1, + const CNcdNodeIdentifier& aNodeId2 ) + { + //DLTRACEIN(("")); + + TInt depth1( 0 ); + TInt depth2( 0 ); + TInt trapError1( KErrNone ); + TInt trapError2( KErrNone ); + + TRAP( trapError1, depth1 = NcdNodeIdentifierEditor::NodeDepthL( aNodeId1 ) ); + TRAP( trapError2, depth2 = NcdNodeIdentifierEditor::NodeDepthL( aNodeId2 ) ); + + // Decide the order according to the depth. If the identifier is not + // an actual nodeidentifier. Then insert it to the beginning of the array + // and sort those identifiers by their lengths. + + if ( trapError1 == KErrNone && trapError2 == KErrNone ) + { + // Both of the given identifiers were for the node. + + // This function returns zero if depths are equal and lengths are equal. + TInt order( depth1 - depth2 ); + if ( depth1 == depth2 ) + { + // Depths are equal. So, check if the id lengths differ. + // If nodeid1 is shorter, then it should be first in the array. + // If they are equal length then the order does not matter. + // If the node1 is longer, then node2 should be first. + order = aNodeId1.NodeId().Length() - aNodeId2.NodeId().Length(); + } + + // Negative number if depth1 is smaller. + // Positive number if id1 is greater. + return order; + } + else if ( trapError1 != KErrNone && trapError2 != KErrNone ) + { + // Because neither was not a node, use the id length to get the + // order. This way the shortest will be the first one etc. + DLERROR(("Neither was node")); + DASSERT( trapError1 == KErrArgument ); + DASSERT( trapError2 == KErrArgument ); + + return aNodeId1.NodeId().Length() - aNodeId2.NodeId().Length(); + } + else if ( trapError1 != KErrNone ) + { + // Here we should get only the KErrArgument that informs that the + // aNodeId1 was not an actual node id. It is most likely some metadata id. + // Insert these in the beginning of the array. So, return negative number + DLERROR(("aNodeId1 not node")); + DASSERT( trapError1 == KErrArgument ); + return -1; + } + else + { + DLERROR(("aNodeId2 not node")); + // Here we should get only the KErrArgument that informs that the + // aNodeId2 was not an actual node id. It is most likely some metadata id. + // Insert these in the beginning of the array. So, return positive number + DASSERT( trapError2 == KErrArgument ); + return 1; + } + } + + +const TUid& CNcdNodeCacheCleaner::ClientUid() const + { + return iGeneralManager.FamilyId(); + } + + +CNcdNodeCacheCleaner::TCleanupState CNcdNodeCacheCleaner::CleanupState() const + { + return iCleanupState; + } + + +void CNcdNodeCacheCleaner::SetAllowCleaning( TBool aAllow ) + { + DLTRACEIN(("")); + if ( aAllow != AllowCleaning() + && !aAllow ) + { + DLINFO(("Cancel on going operation because cleaning is not allowed anymore.")) + Cancel(); + } + iAllowCleaning = aAllow; + } + + +TBool CNcdNodeCacheCleaner::AllowCleaning() const + { + return iAllowCleaning; + } + + +void CNcdNodeCacheCleaner::StartCleanupL() + { + DLTRACEIN(("")); + + // Set the last cleanup time here. So, next automatic start will + // occure after the specified period. + iLastCleanupTime.HomeTime(); + + #ifdef NCD_NODE_CACHE_CLEANER_TURN_OFF + // Nothing to do here. Because cleaning should not be done. + DLWARNING(("*** CNcdNodeCacheCleaner turned off ***")); + #warning *** CNcdNodeCacheCleaner turned off *** + return; + #endif + + if ( !AllowCleaning() ) + { + DLINFO(("Cleaning is not allowed.")); + return; + } + + if ( CleanupState() != ENotStarted + || IsActive() ) + { + DLINFO(("Cleaning already started")); + return; + } + + if ( iResetPriority ) + { + // The priority may have been increased before. + // But it has been marked to be resetted when possible. + // So, reset it here. We come here only if Cancel was + // called when the object was active. + SetPriority( KDefaultCleaningPriority ); + iResetPriority = EFalse; + } + + // Update the node items list. + // This is an initial list that will be used when the nodes are examined. + // The node items list should not be updated anywhere else but here or + // in the start clean excess function. If the array is updated somewhere else, it + // may result an infinite loop if some deepest nodes cannot be removed when + // excess cleaning is done and those nodes are in the end of the list all the time. + // Notice, that it does not matter if all the nodes are not check this time. They + // will be checked next time cleaning is started. Also, if some nodes are missing + // in the list, wrong nodes will not be deleted because parent and child checks + // are done by using the other array such as do not remove and wait arrays. + SetAllDbStorageNodeItemsL(); + + // Cleaning special cases is the first thing to do when cleanup is started. + iCleanupState = ECleaningSpecialCases; + + // Mark this active object to be ready for action. + // Instead of using some other class object to do the job, + // this active object handles everything itself. New job + // is started everytime when the RunL is called until there is + // nothing to be done. Or, until user cancels or stops the action. + iStatus = KRequestPending; + + // Let the active object know that RunL can be called when the + // action is completed. + SetActive(); + + // Because there is not anything special to be done here. + // Set the action complete. So, the RunL will be called. + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::StopCleanup() + { + DLTRACEIN(("")); + + if ( CleanupState() != ENotStarted + || IsActive() ) + { + DLINFO(("Stop cleanup")); + Cancel(); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CheckDbSizeL() + { + DLTRACEIN(("")); + + // If the db size has been exceeded, then + // start the cleaning operation. + // This function may be called for example + // when new nodes have been added to the db. + TInt dbSize( + NodeDbManager().StorageSizeL( ClientUid(), + *iDoNotCleanNameSpaces ) ); + + TTime currentTime; + currentTime.HomeTime(); + TBool timePassed( EFalse ); + if ( iLastCleanupTime + KCheckPeriod < currentTime ) + { + timePassed = ETrue; + } + + // Start cleaning if the database size is too large + // or if it is too long time from the last cleanup. + if ( DbMaxSize() < dbSize ) + { + DLINFO(("Too much data in db: %d", dbSize)); + + // Because the db size has been exceeded we want to clean + // the db with higher priority than usually. + // When the cleanup finishes, the priority should be set back + // to lower level. + if ( IsActive() ) + { + if ( DbMaxSize() * KMaxDbSizeRatio < dbSize ) + { + DLINFO(("Highest priority should be used db size is way too big")); + // Set the priority to max, the next time RunL is called + // because the cleaner is not doing its work efficiently enough + iIncreasePriority = KMaxCleaningPriority; + } + else + { + DLINFO(("Increased priority should be used")); + // Set the priority higher, the next time + // RunL is called. The db size is too large but does not + // exceed the "panic" level. + iIncreasePriority = KIncreasedCleaningPriority; + } + } + else if ( DbMaxSize() * KMaxDbSizeRatio < dbSize ) + { + DLINFO(("Not active. Max priority set")); + SetPriority( KMaxCleaningPriority ); + } + else + { + DLINFO(("Not active. Increased priority set")); + SetPriority( KIncreasedCleaningPriority ); + } + + // To be sure that the cleanup does not reset the priority. + // No need to reset it because new values have been given above. + iResetPriority = EFalse; + + // Start the actual cleaning. + StartCleanupL(); + } + else if ( timePassed ) + { + DLINFO(("Time passed")); + // Start the actual cleaning but no need to increase the priority. + StartCleanupL(); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::AddCleanupIdentifiersL( const RPointerArray& aIdentifiers ) + { + DLTRACEIN(("")); + + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + AddCleanupIdentifierL( *aIdentifiers[ i ] ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeCacheCleaner::AddCleanupIdentifierL( const CNcdNodeIdentifier& aIdentifier ) + { + DLTRACEIN(("")); + DLNODEID(( aIdentifier )); + if ( ContainsIdentifier( aIdentifier, iDoNotRemoves ) ) + { + // Do not remove list already contains the given identifier. + // So, insert the given identifier to the wait list. + // Notice that identifier should be left also into the do not remove list. + AddIdentifierL( aIdentifier, iWaitingIdentifiers ); + } + else if ( !NcdNodeIdentifierUtils::ContainsIdentifier( + aIdentifier, iWaitingIdentifiers ) && + !NcdNodeIdentifierUtils::ContainsIdentifier( + aIdentifier, iWaitingParentIdentifiers ) ) + { + // Just insert the given identifier into the cleanup list unless + // it already existed in some waiting array or in do not remove list. + AddIdentifierL( aIdentifier, iCleanupIdentifiers ); + } + + // No need to check the waiting parents, because it will be checked when items + // are going to be removed. + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::RemoveCleanupIdentifiers( const RPointerArray& aIdentifiers ) + { + DLTRACEIN(("")); + + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + RemoveCleanupIdentifier( *aIdentifiers[ i ] ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeCacheCleaner::RemoveCleanupIdentifier( const CNcdNodeIdentifier& aIdentifier ) + { + DLTRACEIN(("")); + + // If wait list contains the given identifier. + // So, remove the given identifier from the wait list. + RemoveIdentifier( aIdentifier, iWaitingIdentifiers ); + + // If wait list contains the given identifier. + // So, remove the given identifier from the wait list. + RemoveIdentifier( aIdentifier, iWaitingParentIdentifiers ); + + // Remove the given identifier from cleanup list + RemoveIdentifier( aIdentifier, iCleanupIdentifiers ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::AddDoNotRemoveIdentifiersL( + const RPointerArray& aIdentifiers, + TBool aCanRemoveParent ) + { + DLTRACEIN(("")); + + // Check if the identifiers should be inserted into the waiting or do not remove + // array. + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + AddDoNotRemoveIdentifierL( *aIdentifiers[ i ], aCanRemoveParent ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeCacheCleaner::AddDoNotRemoveIdentifierL( + const CNcdNodeIdentifier& aIdentifier, + TBool aCanRemoveParent ) + { + DLTRACEIN(("")); + DLNODEID(( aIdentifier )); + + if ( RemoveIdentifier( aIdentifier, iCleanupIdentifiers ) ) + { + // Cleanup list already contained the given identifier. + // So, insert the given identifier to the wait list. + AddIdentifierL( aIdentifier, iWaitingIdentifiers ); + } + + // Insert the given identifier into the do not remove list. + // It should be ther until RemoveDoNotRemoveIdentifiers is used. + // So, even if identifier is in waiting list it will be also, + // in the do not remove list. + + // Compare given identifier to the identifiers in the array. + TInt i = 0; + for ( ; i < iDoNotRemoves.Count(); ++i ) + { + if ( iDoNotRemoves[ i ]->Key().Equals( aIdentifier ) ) + { + // Flag is only updated if it goes from EFalse -> ETrue + if ( aCanRemoveParent ) + { + DLTRACE(( _L("Updating parent removal flag for: %S"), + &iDoNotRemoves[ i ]->Key().NodeId() )); + // The given identifier was found from the array + // update + iDoNotRemoves[ i ]->SetValue( ETrue ); + } + break; + } + } + + + if ( i == iDoNotRemoves.Count() ) + { + DLTRACE(("Adding a non-removable identifier")); + CNcdNodeIdentifier* id = CNcdNodeIdentifier::NewLC( aIdentifier ); + // ownership is transferred + CDoNotRemoveIdentifier* pair = new ( ELeave ) + CDoNotRemoveIdentifier( id, aCanRemoveParent ); + CleanupStack::Pop( id ); + + CleanupStack::PushL( pair ); + iDoNotRemoves.AppendL( pair ); + CleanupStack::Pop( pair ); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::RemoveDoNotRemoveIdentifiersL( const RPointerArray& aIdentifiers ) + { + DLTRACEIN(("")); + + // Check if the identifiers should be moved from the waiting back to the + // cleanup array and remove identifier from the do not remove array. + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + RemoveDoNotRemoveIdentifierL( *aIdentifiers[ i ] ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeCacheCleaner::RemoveDoNotRemoveIdentifierL( + const CNcdNodeIdentifier& aIdentifier, + TBool aForceRemove ) + { + DLTRACEIN(("")); + DLNODEID(( aIdentifier )); + + TBool waitingRemoved( + RemoveIdentifier( aIdentifier, iWaitingIdentifiers ) ); + TBool waitingParentRemoved( + RemoveIdentifier( aIdentifier, iWaitingParentIdentifiers ) ); + + if ( waitingRemoved || waitingParentRemoved ) + { + // Wait list contained the given identifier. + // So, move the given identifier back to the cleanup list. + AddIdentifierL( aIdentifier, iCleanupIdentifiers ); + } + + RemoveIdentifier( aIdentifier, iDoNotRemoves, aForceRemove ); + + DLTRACEOUT(("")); + } + + +TInt CNcdNodeCacheCleaner::DbMaxSize() const + { + DLTRACEIN(("")); + return iDbMaxSize; + } + + +void CNcdNodeCacheCleaner::SetDbMaxSize( const TInt aDbMaxSize ) + { + DLTRACEIN(("")); + iDbMaxSize = aDbMaxSize; + } + + +void CNcdNodeCacheCleaner::ForceCleanupL() + { + DLTRACEIN(("")); + + // Print array debug infos into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iDoNotRemoves array:")); + ArrayDebugPrint( iDoNotRemoves ); + + DLINFO(("iWaitingIdentifiers array:")); + ArrayDebugPrint( iWaitingIdentifiers ); + + DLINFO(("iWaitingParentIdentifiers array:")); + ArrayDebugPrint( iWaitingParentIdentifiers ); + #endif + + + // iDoNotRemoves array is not reseted here. So, those nodes will not be deleted even here. + // This way some nodes can be saved from deletion. But, the nodes that are waiting to + // be deleted are forced to be deleted now. + + // Move waiting identifiers to the cleanup array. + AddIdentifiersL( iWaitingIdentifiers, iCleanupIdentifiers ); + iWaitingIdentifiers.ResetAndDestroy(); + + AddIdentifiersL( iWaitingParentIdentifiers, iCleanupIdentifiers ); + iWaitingParentIdentifiers.ResetAndDestroy(); + + // Clean the items that are in the cleanup array from db. + HandleCleaningL(); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::HandleCleaningL() + { + DLTRACEIN(("")); + + // Asynchronous operations have to be stopped before + // starting synchronous cleaning. Otherwise the + // asynchronous cleaning may work wrong later. + Cancel(); + + // Update the arrays and handle cleaning. + // Thde node array will be emptied when this is done. + HandleCleaningL( ETrue, ETrue ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::HandleCleaningL( TBool aReloadNodeList, TBool aResetNodeList ) + { + DLTRACEIN(("")); + + #ifdef NCD_NODE_CACHE_CLEANER_TURN_OFF + // Nothing to do here. Because cleaning should not be done. + DLWARNING(("*** CNcdNodeCacheCleaner turned off ***")); + #warning *** CNcdNodeCacheCleaner turned off *** + return; + #endif + + if ( !AllowCleaning() ) + { + DLINFO(("Cleaning is not allowed.")); + return; + } + + // Clean all the nodes from the list. + // Notice that we only delete the nodes here. + // The metadata and metadata related + // data will be removed later, when this cleaner checks + // if they are left hanging and + // do not belong to any node anymore. + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iCleanupIdentifiers array before parent and child check:")); + ArrayDebugPrint( iCleanupIdentifiers ); + #endif + + if ( aReloadNodeList ) + { + DLINFO(("Reload node list")); + // The caller may require the list to be updated. + // So, make sure that all the necessary ids are in the + // list. It may also be so, that the db is empty. + // This check is required for example if some other class wants to + // clean some nodes. + SetAllDbStorageNodeItemsL(); + } + + if ( iAllDbStorageNodeItems.Count() == 0 ) + { + // Because there are not nodeitems in the db, + // just return. + DLTRACEOUT(("No nodes to remove")); + return; + } + + // Because the name of the children starts with the same id + // as the name of the parent, check that also the children + // of the parents will be deleted, unless they are in do not remove list + // or in the waiting list. + AddChildrenToCleanupArrayL(); + ParentCleaningCheckL(); + + // In some cases there are unremovable items in the cleanup array + // so let's remove them + RemoveDoNotRemovesFromCleanupArray(); + + DLINFO(("Remove nodes from db")); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iCleanupIdentifiers array:")); + ArrayDebugPrint( iCleanupIdentifiers ); + #endif + + // Now the parents and their children are in the array. + NodeDbManager(). + RemoveDataFromDatabaseL( iCleanupIdentifiers, + iNodeCleanupTypes ); + + // Because all the items have been marked as removable in db + // manager, we can clear the array. + iCleanupIdentifiers.ResetAndDestroy(); + + if ( aResetNodeList ) + { + DLINFO(("Reset node list")); + // The list should be reseted to save some memory. + iAllDbStorageNodeItems.ResetAndDestroy(); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::DoCancel() + { + DLTRACEIN(("")); + + // Do not continue the action any more + // because cancel was called. + ResetState(); + + // Also, make sure that the active object will know that the + // cancel is ready. If active process is going on, the status is + // set to KRequestPending. + + // There is no need for additional User::RequestComplete + // call here because DoCancel is executed only if this object is active + // and there has always been a call to User::RequestComplete when this + // object has been put to active state in other function calls. + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::RunL() + { + DLTRACEIN(("")); + + if ( iIncreasePriority != KDefaultCleaningPriority ) + { + // Increase priority flag has been set. + // So, increase the priority from idle to suggested value. + SetPriority( iIncreasePriority ); + iIncreasePriority = KDefaultCleaningPriority; + } + + User::LeaveIfError( iStatus.Int() ); + + // Continue working if there is something to be done. + switch ( iCleanupState ) + { + case ECleaningSpecialCases: + DLINFO(("KErrNone cleaning specials")); + CleanSpecialsL(); + break; + + case ECleaningTemporaryNodes: + DLINFO(("KErrNone cleaning temporary nodes")); + CleanTemporaryNodesL(); + break; + + case ECleaningHangingCases: + DLINFO(("KErrNone cleaning hangings")); + CleanHangingsL(); + break; + + case EStartCleaningHangingSpecialCases: + DLINFO(("KErrNone start cleaning special hangings")); + StartCleanSpecialHangingsL(); + break; + + case ECleaningHangingSpecialCases: + DLINFO(("KErrNone cleaning special hangings")); + CleanSpecialHangingsL(); + break; + + case EFinishCleaningHangingSpecialCases: + DLINFO(("KErrNone finish cleaning special hangings")); + FinishCleanSpecialHangingsL(); + break; + + case EStartCleaningExcess: + DLINFO(("KErrNone start cleaning excess")); + StartCleanExcessL(); + break; + + case EStartRootChildrenCheck: + DLINFO(("KErrNone start root children check")); + StartRootChildrenCheckL(); + break; + + case ECheckRootChildren: + DLINFO(("KErrNone check root children")); + CheckRootChildrenL(); + break; + + case EHandleBundleChildren: + DLINFO(("KErrNone handle bundle children")); + HandleBundleChildrenL(); + break; + + case ECleaningExpireds: + DLINFO(("KErrNone cleaning expireds")); + CleanExpiredsL(); + break; + + case ECleaningExcess: + DLINFO(("KErrNone cleaning excess")); + CleanExcessL(); + break; + + case EStopping: + DLINFO(("KErrNone EStopping")); + StoppingL(); + break; + + case ENotStarted: + DLINFO(("KErrNone ENotStarted")); + // Nothing to do here + break; + + default: + DLERROR(("KErrNone default")); + DASSERT( EFalse ); + break; + } + + DLTRACEOUT(("")); + } + + +TInt CNcdNodeCacheCleaner::RunError( TInt aError ) + { + DLTRACEIN(( "aError: %d", aError )); + (void) aError; + ResetState(); + return KErrNone; + } + +CNcdNodeManager& CNcdNodeCacheCleaner::NodeManager() const + { + return iNodeManager; + } + + +CNcdNodeDbManager& CNcdNodeCacheCleaner::NodeDbManager() const + { + return iNodeDbManager; + } + + +CNcdNodeFactory& CNcdNodeCacheCleaner::NodeFactory() const + { + return iNodeFactory; + } + + +TBool CNcdNodeCacheCleaner::ContainsIdentifier( + const CNcdNodeIdentifier& aIdentifier, + const RPointerArray& aTargetIdentifiers ) const + { + const TInt count = aTargetIdentifiers.Count(); + // Compare given identifier to the identifiers in the array. + for ( TInt i = 0; i < count; ++i ) + { + if ( aTargetIdentifiers[ i ]->Key().Equals( aIdentifier ) ) + { + // The given identifier was found from the array + return ETrue; + } + } + // Identifier was not found. + return EFalse; + } + + +TBool CNcdNodeCacheCleaner::AddIdentifiersL( const RPointerArray& aIdentifiers, + RPointerArray& aTargetArray ) + { + DLTRACEIN(("")); + + TBool targetArrayChanged( EFalse ); + TBool identifierAdded( EFalse ); + const TInt count = aIdentifiers.Count(); + + for ( TInt i = 0; i < count; ++i ) + { + DASSERT( aIdentifiers[ i ] != NULL ); + + identifierAdded = + AddIdentifierL( *aIdentifiers[ i ], + aTargetArray ); + + if ( !targetArrayChanged && identifierAdded ) + { + DLINFO(("Identifier was added")); + // Because the identifier was added into the array. + // This is set only once because if something was added + // then the array has been changed. + // But, let the loop add all the other identifiers also. + // So, do not break here. + targetArrayChanged = ETrue; + } + } + + DLTRACEOUT(("")); + + // Inform the called if something was added into the target array. + return targetArrayChanged; + } + +TBool CNcdNodeCacheCleaner::AddIdentifierL( const CNcdNodeIdentifier& aIdentifier, + RPointerArray& aTargetArray ) + { + DLTRACEIN(("")); + + // Compare the given identifier to the identifiers that have already inserted into the + // array. + const TInt count = aTargetArray.Count(); + for ( TInt i = 0; i < count; ++i ) + { + DASSERT( aTargetArray[ i ] != NULL ); + + // If the identifier was already in the array then, do not add it again. + if ( aIdentifier.Equals( *aTargetArray[ i ] ) ) + { + DLINFO(("Identifier was found. Return false.")); + // The identifier was found. + // So, return EFalse to inform that identifier was not added. + return EFalse; + } + } + + // The item was not in the array yet. So, append it into the array. + + CNcdNodeIdentifier* copyIdentifier = + CNcdNodeIdentifier::NewLC( aIdentifier ); + aTargetArray.AppendL( copyIdentifier ); + CleanupStack::Pop( copyIdentifier ); + + DLTRACEOUT(("")); + + // Return ETrue, because addition was done into the target array. + return ETrue; + } + + +TBool CNcdNodeCacheCleaner::RemoveIdentifiers( const RPointerArray& aIdentifiers, + RPointerArray& aTargetArray ) + { + DLTRACEIN(("")); + + TBool targetArrayChanged( EFalse ); + TBool identifierRemoved( EFalse ); + const TInt count = aIdentifiers.Count(); + for ( TInt i = 0; i < count; ++i ) + { + DASSERT( aIdentifiers[ i ] != NULL ); + + identifierRemoved = + RemoveIdentifier( *aIdentifiers[ i ], + aTargetArray ); + + if ( !targetArrayChanged && identifierRemoved ) + { + DLINFO(("Identifier was removed")); + // The identifier was removed from the array. + // This value is set only once because if something was added + // then the array has been changed. + targetArrayChanged = ETrue; + } + } + + DLTRACEOUT(("")); + + // Inform the caller if something was added into the target array. + return targetArrayChanged; + } + +TBool CNcdNodeCacheCleaner::RemoveIdentifier( const CNcdNodeIdentifier& aIdentifier, + RPointerArray& aTargetArray ) + { + DLTRACEIN(("")); + + TBool removed( EFalse ); + + // Remove all occurrences of the identifier from the target array. + // Start from the end of the array and move towards the beginning. + for ( TInt i = aTargetArray.Count() - 1; i >= 0; --i ) + { + DASSERT( aTargetArray[ i ] != NULL ); + + if ( aIdentifier.Equals( *aTargetArray[ i ] ) ) + { + DLINFO(("Remove identifier")); + // The identifier was found from the array. So, remove it. + delete aTargetArray[ i ]; + aTargetArray.Remove( i ); + removed = ETrue; + // Because the indexing comes from the counter value towards + // the zero, no need to update indexing here even if the removal + // was done above. + } + } + + DLTRACEOUT(("")); + + return removed; + } + + +TBool CNcdNodeCacheCleaner::RemoveIdentifier( + const CNcdNodeIdentifier& aIdentifier, + RPointerArray& aTargetArray, + TBool aForceRemove ) + { + DLTRACEIN(("")); + // Remove all occurrences of the identifier from the target array. + // Start from the end of the array and move towards the beginning. + TInt i = aTargetArray.Count(); + while ( i-- ) + { + DASSERT( aTargetArray[ i ] != NULL ); + + if ( aIdentifier.Equals( aTargetArray[ i ]->Key() ) ) + { + // Make sure that favorites are removed only if they are actually + // removed from favorites + if ( aForceRemove || + !aTargetArray[ i ]->Value() ) + { + DLINFO(("Remove identifier")); + // The identifier was found from the array. So, remove it. + delete aTargetArray[ i ]; + aTargetArray.Remove( i ); + } + return ETrue; + } + } + + DLTRACEOUT(("")); + return EFalse; + } + + +void CNcdNodeCacheCleaner::SetAllDbStorageNodeItemsL() + { + DLTRACEIN(("")); + + // Just in case, first clean the identifier list. + iAllDbStorageNodeItems.ResetAndDestroy(); + + // Yes, we intentionally push a member variable to the cleanupstack + CleanupResetAndDestroyPushL( iAllDbStorageNodeItems ); + // Get the new values for cleaning. + // Note that some of the items are not accepted + // for this list. + + NodeDbManager(). + GetAllClientItemIdentifiersL( + iAllDbStorageNodeItems, + ClientUid(), + *iDoNotCleanNameSpaces, + iNodeCleanupTypes ); + + CleanupStack::Pop( &iAllDbStorageNodeItems ); + // Sort the identifiers in the array. + // The first item is closest to the root. The last item is + // deepest in the hierarchy. + // Give the function pointer of the static function as a parameter. + + DLTRACE(("start sorting")); + TLinearOrder sorter( &NodeIdentifierArraySortById ); + iAllDbStorageNodeItems.Sort( sorter ); + DLTRACE(("sorting ended")); + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iAllDbStorageNodeItems array:")); + ArrayDebugPrint( iAllDbStorageNodeItems ); + #endif + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::SetAllDbStorageIconItemsL() + { + DLTRACEIN(("")); + + // Just in case first clean the identifier list. + iAllDbIconItems.ResetAndDestroy(); + + // Yes, we intentionally push a member variable to the cleanupstack + CleanupResetAndDestroyPushL( iAllDbIconItems ); + + // Get the new values for cleaning. + // Note that some of the items are not accepted + // for this list. + + NodeDbManager(). + GetAllClientItemIdentifiersL( + iAllDbIconItems, + ClientUid(), + *iDoNotCleanNameSpaces, + iIconCleanupTypes ); + + CleanupStack::Pop( &iAllDbIconItems ); + // Icon ids do not need to be sorted, because they do not contain node ids but + // are something else. + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iAllDbIconItems array:")); + ArrayDebugPrint( iAllDbIconItems ); + #endif + + DLTRACEOUT(("")); + } + + + +void CNcdNodeCacheCleaner::SetAllDbStorageScreenshotItemsL() + { + DLTRACEIN(("")); + // Just in case first clean the identifier list. + iAllDbScreenshotItems.ResetAndDestroy(); + + // Yes, we intentionally push a member variable to the cleanupstack + CleanupResetAndDestroyPushL( iAllDbScreenshotItems ); + + // Get the new values for cleaning. + // Note that some of the items are not accepted + // for this list. + NodeDbManager(). + GetAllClientItemIdentifiersL( + iAllDbScreenshotItems, + ClientUid(), + *iDoNotCleanNameSpaces, + iScreenshotCleanupTypes ); + + CleanupStack::Pop( &iAllDbScreenshotItems ); + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iAllDbScreenshotItems array:")); + ArrayDebugPrint( iAllDbScreenshotItems ); + #endif + } + + +void CNcdNodeCacheCleaner::AddChildrenToCleanupArrayL() + { + DLTRACEIN(("")); + + // Get the counter value. So, we know how many items are + // in the list before children are added + TInt counter = iCleanupIdentifiers.Count(); + + // Check if any children are found from the db items. + // If they are found, then move them to the cleanup array. + CNcdNodeIdentifier* cleanupIdentifier( NULL ); + CNcdNodeIdentifier* storageItemIdentifier( NULL ); + for ( TInt i = 0; i < counter; ++i ) + { + cleanupIdentifier = iCleanupIdentifiers[ i ]; + // Start from the end of the list. So, the indexing is + // always correct even if an item is moved + for ( TInt j = iAllDbStorageNodeItems.Count(); j > 0; --j ) + { + storageItemIdentifier = iAllDbStorageNodeItems[ j - 1 ]; + if ( NcdNodeIdentifierEditor::NodeDepthL( *cleanupIdentifier ) + > NcdNodeIdentifierEditor::NodeDepthL( *storageItemIdentifier ) ) + { + // Because the db items array is sorted so that the deepest items are + // in the end of the array, the children can not be found anymore. + // (Notice that db items are gone through from the end of the array.) + DLINFO(("Child parent barrier passed: %d", j - 1 )); + break; + } + else if ( NcdNodeIdentifierEditor::ParentOf( *cleanupIdentifier, + *storageItemIdentifier ) + && !ContainsIdentifier( *storageItemIdentifier, + iDoNotRemoves ) + && !NcdNodeIdentifierUtils::ContainsIdentifier( + *storageItemIdentifier, iWaitingIdentifiers ) + && !NcdNodeIdentifierUtils::ContainsIdentifier( + *storageItemIdentifier, iWaitingParentIdentifiers ) ) + { + DLINFO(("Children items found and can be moved")); + // The child is not in do not remove list or in the waiting list. + // So, it can be inserted into the cleanup list. + // Notice that before cleaning it should + // be checked that the child is not a parent of some other child that + // can not be removed. + // Use this function to check that the item is not appended twice + AddCleanupIdentifierL( *storageItemIdentifier ); + // Remove the data from its original place. Notice that identifier + // is copied in the function above. + delete storageItemIdentifier; + iAllDbStorageNodeItems.Remove( j - 1 ); + } + } + } + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iCleanupIdentifiers array:")) + ArrayDebugPrint( iCleanupIdentifiers ); + #endif + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::ParentCleaningCheckL() + { + DLTRACEIN(("")); + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iDoNotRemoves array:")); + ArrayDebugPrint( iDoNotRemoves ); + #endif + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iWaitingParentIdentifiers array:")); + ArrayDebugPrint( iWaitingParentIdentifiers ); + #endif + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iWaitingIdentifiers array:")); + ArrayDebugPrint( iWaitingIdentifiers ); + #endif + + // Check if there are some parents that are going to be cleaned but + // they should not be because their children are in the do not remove list + // or in the in the waiting list + for ( TInt i = iCleanupIdentifiers.Count(); i > 0 ; --i ) + { + for ( TInt j = 0; j < iDoNotRemoves.Count(); ++j ) + { + // first check if parent can not be removed (for favorites it can be removed) + if ( !iDoNotRemoves[ j ]->Value() && + NcdNodeIdentifierEditor::ParentOf( *iCleanupIdentifiers[ i - 1 ], + iDoNotRemoves[ j ]->Key() ) ) + { + DLINFO(("Do not remove parent because child is do not remove: %d", i)) + // Because the cleanup identifier was a parent of something that + // should not be removed, then do not remove the parent either, + // but insert it to wait. + AddIdentifierL( *iCleanupIdentifiers[ i - 1 ], iWaitingParentIdentifiers ); + delete iCleanupIdentifiers[ i - 1 ]; + iCleanupIdentifiers.Remove( i - 1 ); + + // Because the identifier was now removed from the array + // and moved to the waiting array, we skip to the next identifier check. + // This way the i-indexing will be correct. + break; + } + } + } + + // Check the waiting identifiers in their own loop, so the i indexing + // will be correct here if removing has been done above. + for ( TInt i = iCleanupIdentifiers.Count(); i > 0 ; --i ) + { + for ( TInt k = 0; k < iWaitingIdentifiers.Count(); ++k ) + { + if ( NcdNodeIdentifierEditor::ParentOf( *iCleanupIdentifiers[ i - 1 ], + *iWaitingIdentifiers[ k ] ) ) + { + DLINFO(("Do not remove parent because child is waiting: %d", i)) + // Because the cleanup identifier was a parent of something that + // should not be removed, then do not remove the parent either, + // but insert it to wait. + AddIdentifierL( *iCleanupIdentifiers[ i - 1 ], + iWaitingParentIdentifiers ); + delete iCleanupIdentifiers[ i - 1 ]; + iCleanupIdentifiers.Remove( i - 1 ); + + // Because the identifier was now removed from the array + // and moved to the waiting array, we skip to the next identifier check. + // This way the i-indexing will be correct. + break; + } + } + } + + // Now check if there are some parents waiting that should not any more + TBool wasNotFound( ETrue ); + for ( TInt i = iWaitingParentIdentifiers.Count(); i > 0 ; --i ) + { + wasNotFound = ETrue; + for ( TInt j = 0; j < iDoNotRemoves.Count(); ++j ) + { + if ( !iDoNotRemoves[ j ]->Value() && + NcdNodeIdentifierEditor::ParentOf( *iWaitingParentIdentifiers[ i - 1 ], + iDoNotRemoves[ j ]->Key() ) ) + { + // The waiting parent should still be waiting + wasNotFound = EFalse; + break; + } + } + for ( TInt k = 0; k < iWaitingIdentifiers.Count() && wasNotFound; ++k ) + { + if ( NcdNodeIdentifierEditor::ParentOf( *iWaitingParentIdentifiers[ i - 1 ], + *iWaitingIdentifiers[ k ] ) ) + { + // The waiting parent should still be waiting + wasNotFound = EFalse; + break; + } + } + + if ( wasNotFound ) + { + DLINFO(("Waiting parent can be moved to the cleanup list: %d", i)); + // Because the waiting identifier was not a parent of something that + // should not be removed anymore. Move it to the cleanup list. + AddIdentifierL( *iWaitingParentIdentifiers[ i - 1 ], iCleanupIdentifiers ); + delete iWaitingParentIdentifiers[ i - 1 ]; + iWaitingParentIdentifiers.Remove( i - 1 ); + } + } + + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iCleanupIdentifiers array:")); + ArrayDebugPrint( iCleanupIdentifiers ); + #endif + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::RemoveDoNotRemovesFromCleanupArray() + { + DLTRACEIN(("")); + TInt doNotRemoveCount = iDoNotRemoves.Count(); + while ( doNotRemoveCount-- ) + { + TInt cleanupCount = iCleanupIdentifiers.Count(); + while( cleanupCount-- ) + { + if ( iDoNotRemoves[ doNotRemoveCount ]->Key().Equals( + *iCleanupIdentifiers[ cleanupCount ] ) ) + { + DLTRACE(("Removing")); + DLNODEID(( *iCleanupIdentifiers[ cleanupCount ] )); + delete iCleanupIdentifiers[ cleanupCount ]; + iCleanupIdentifiers.Remove( cleanupCount ); + break; + } + } + } + } + + +void CNcdNodeCacheCleaner::CheckTemporaryNodesL() + { + DLTRACEIN(("")); + + TInt itemCount( iAllDbStorageNodeItems.Count() ); + + CNcdNodeIdentifier* identifier( NULL ); + + for ( TInt i = itemCount; i > 0; --i ) + { + identifier = iAllDbStorageNodeItems[ i - 1 ]; + + // All the nodes that do not start with the root id are thought to be + // temporary nodes. + if ( NcdNodeIdentifierEditor::IdentifiesTemporaryNodeL( *identifier ) ) + { + DLINFO((_L("Remove temporary node: %S, %S, %S, %d"), + &identifier->NodeNameSpace(), &identifier->NodeId(), + &identifier->ServerUri(), identifier->ClientUid().iUid)); + // This is a temporary node. + // Insert it into the cleanup array if removal is allowed. + AddCleanupIdentifierL( *identifier ); + + // Remove the data from its original place. Notice that identifier + // is copied in the function above. + delete iAllDbStorageNodeItems[ i - 1 ]; + iAllDbStorageNodeItems.Remove( i - 1 ); + } + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::ResetState() + { + DLTRACEIN(("")); + + iExpiredCleaningIndex = 0; + iExcessCleanupStarted = EFalse; + + // Do not waste space with old info. + iAllDbStorageNodeItems.ResetAndDestroy(); + iAllDbIconItems.ResetAndDestroy(); + iDbMetaIdentifiers.ResetAndDestroy(); + iRootChildren.ResetAndDestroy(); + iBundleChildren.ResetAndDestroy(); + + iAllDbScreenshotItems.ResetAndDestroy(); + + // Set the priority to the correct value + // for the next cleaning round. The priority + // may have been changed if db was too full. + iIncreasePriority = KDefaultCleaningPriority; + if ( IsActive() ) + { + DLINFO(("Active. Priority should be reseted later.")); + // Set the priority flags for the member variables. + // This value will be used in later in StartCleanupL + // where the priority is set for this active object next time + // they are called. This check is required because DoCancel uses + // this function and then the active object may still be active until + // DoCancel finishes its job. + iResetPriority = ETrue; + } + else + { + DLINFO(("Not active")); + // Set the correct priority value. + // The priority can also be set for the active object because + // it is not active at the moment. + SetPriority( KDefaultCleaningPriority ); + } + + // Set state and other flags. To inital values. + iCleanupState = ENotStarted; + + DLTRACEOUT(("")); + } + + +#ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT +void CNcdNodeCacheCleaner::ArrayDebugPrint( const RPointerArray& aIdentifiers ) const + { + DLTRACEIN(("")); + + CNcdNodeIdentifier* identifier( NULL ); + (void) *identifier; // prevents compiler warning of unused variable + + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + identifier = aIdentifiers[ i ]; + DLINFO((_L("Array identifier: %S, %S, %S, %d"), + &identifier->NodeNameSpace(), &identifier->NodeId(), + &identifier->ServerUri(), identifier->ClientUid().iUid)); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeCacheCleaner::ArrayDebugPrint( const RPointerArray& aIdentifiers ) const + { + DLTRACEIN(("")); + + const CNcdNodeIdentifier* identifier( NULL ); + (void) *identifier; // prevents compiler warning of unused variable + + for ( TInt i = 0; i < aIdentifiers.Count(); ++i ) + { + identifier = &aIdentifiers[ i ]->Key(); + DLINFO((_L("Array identifier: %S, %S, %S, %d"), + &identifier->NodeNameSpace(), &identifier->NodeId(), + &identifier->ServerUri(), identifier->ClientUid().iUid)); + } + + DLTRACEOUT(("")); + } + +#endif // NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + + +void CNcdNodeCacheCleaner::CleanSpecialsL() + { + DLTRACEIN(("")); + + // Try to clean some nodes if they are in the cleanuplist. + // Do not reset the node list or delete it here. + // This list is used throughout the cleaning process. + // The array might be updated only in the start excess cleaning function. + // Other functions should not update the list, because otherwise the + // excess cleaner may go into the infinite loop if deepest nodes cannot + // be removed. + HandleCleaningL( EFalse, EFalse ); + + // Next step will be cleaning temporary nodes that + // are not used any more. If too much space is used, + // some extra cleaning will also be done by looping these steps. + iCleanupState = ECleaningTemporaryNodes; + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CleanTemporaryNodesL() + { + DLTRACEIN(("")); + + // Notice that the iAllDbStorageNodeItems may not be up to date here if + // some new nodes have been inserted into the database after the cleaning + // has been started. So, if those new items are temporary objects they will + // not be checked here. The update of the array is omitted here to save some + // time, because db-actions may take long time. Also, the array should not + // be updated here, because otherwise excess removing may go into the infinite loop. + // The omitting of some temporary nodes does not matter because they will be + // cleaned next time the cleaner starts it actions. + CheckTemporaryNodesL(); + + // HandleCleaningL and the functions it uses can handle the removing of the + // item from the db array. + // Just use the old node list. + // Do not delte the list because if excess cleaning is done, then the list should + // not be reloaded. Reloading might cause a infinite loop. + HandleCleaningL( EFalse, EFalse ); + + iCleanupState = ECleaningHangingCases; + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CleanHangingsL() + { + DLTRACEIN(("")); + + // The node items list has to be updated here. So, we can be sure that + // both the node array and metadata array are up to date when hangings + // are searched. If node list were not up to date, then some of the + // the metadata may be mistakenly thought to be hanging. + + // Get the node identifiers for the list. We do not use the member variable + // iAllDbStorageNodeItems here, because it should only be updated when the + // cleaning is started or in the excess clean function. + // Notice that here it does not matter that the nodes are not in specific + // order. Because all the nodes are compared to metadatas. + RPointerArray dbNodeIdentifiers; + CleanupResetAndDestroyPushL( dbNodeIdentifiers ); + NodeDbManager(). + GetAllClientItemIdentifiersL( + dbNodeIdentifiers, + ClientUid(), + *iDoNotCleanNameSpaces, + iNodeCleanupTypes ); + + // Get all the metadata identifiers from the db. + // (left the do not clean namespaces out because they + // are ignored in all cases here) + RPointerArray dbMetaIdentifiers; + CleanupResetAndDestroyPushL( dbMetaIdentifiers ); + NodeDbManager(). + GetAllClientItemIdentifiersL( + dbMetaIdentifiers, + ClientUid(), + *iDoNotCleanNameSpaces, + iMetaCleanupTypes ); + + DLINFO(("Db node count: %d", dbNodeIdentifiers.Count())); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("dbNodeIdentifiers array:")); + ArrayDebugPrint( dbNodeIdentifiers ); + #endif + + DLINFO(("Db meta count: %d", dbMetaIdentifiers.Count())); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("dbNodeIdentifiers array:")); + ArrayDebugPrint( dbMetaIdentifiers ); + #endif + + // Check if the node corresponding metadata identifier can be + // found from the db node list. + // If it can not be found, then the item has been left to hang in + // the db and can be removed. + CNcdNodeIdentifier* identifier( NULL ); + TBool nodeFound( EFalse ); + TInt j = dbMetaIdentifiers.Count(); + const TInt count = dbNodeIdentifiers.Count(); + + while ( j-- ) + { + nodeFound = EFalse; + + for ( TInt i = 0; i < count; ++i ) + { + // Get the metadata part of the node name + identifier = + NcdNodeIdentifierEditor:: + CreateMetaDataIdentifierL( *dbNodeIdentifiers[ i ] ); + + // Check if the metadata part of the node id + // equals the metadata part of the list. + if ( identifier->Equals( *dbMetaIdentifiers[ j ] ) ) + { + // The identifiers equal so corresponding node was found + nodeFound = ETrue; + + delete identifier; + identifier = NULL; + + // Because node was found the meta can be removed from the + // list + delete dbMetaIdentifiers[ j ]; + dbMetaIdentifiers.Remove( j ); + + break; + } + else + { + delete identifier; + identifier = NULL; + } + } + + if ( !nodeFound ) + { + // If node was not found from DB, it is possibly in DoNotRemove list. + // Don't delete the metadata of such a node. + const TInt doNotRemovesCount = iDoNotRemoves.Count(); + for ( TInt i = 0; i < doNotRemovesCount; i++ ) + { + identifier = + NcdNodeIdentifierEditor::CreateMetaDataIdentifierL( + iDoNotRemoves[i]->Key() ); + + // Check if the metadata part of the node id equals the metadata part + // of the list. + if ( identifier->Equals( *dbMetaIdentifiers[ j ] ) ) + { + + delete identifier; + identifier = NULL; + + delete dbMetaIdentifiers[ j ]; + dbMetaIdentifiers.Remove( j ); + + break; + } + else + { + delete identifier; + identifier = NULL; + } + } + } + } + + DLINFO(("Remove hangings from db: %d", dbMetaIdentifiers.Count())); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("dbMetaIdentifiers array:")); + ArrayDebugPrint( dbMetaIdentifiers ); + #endif + + // No need to check do not remove items here, because all the items + // that are hanging are independent from the nodes. That is also why, + // we do not need to do any parent child relation checking either. + NodeDbManager(). + RemoveDataFromDatabaseL( dbMetaIdentifiers, + iMetaCleanupTypes ); + + CleanupStack::PopAndDestroy( &dbMetaIdentifiers ); + CleanupStack::PopAndDestroy( &dbNodeIdentifiers ); + + // Next handle excess cleaning + iCleanupState = EStartCleaningHangingSpecialCases; + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::StartCleanSpecialHangingsL() + { + DLTRACEIN(("")); + + // Notice that the special hanging only need metadata objects. + + // Get all the metadata identifiers from the db. + // (left the do not clean namespaces out because they + // are ignored in all cases here) + // Notice that the icon array is also updated below. So, + // the metadata and the icon infromation will be in sync. + iDbMetaIdentifiers.ResetAndDestroy(); + NodeDbManager(). + GetAllClientItemIdentifiersL( + iDbMetaIdentifiers, + ClientUid(), + *iDoNotCleanNameSpaces, + iMetaCleanupTypes ); + + DLINFO(("Db meta count: %d", iDbMetaIdentifiers.Count())); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iDbMetaIdentifiers array:")); + ArrayDebugPrint( iDbMetaIdentifiers ); + #endif + + // Start to clean all the special cases. + SetAllDbStorageIconItemsL(); + + SetAllDbStorageScreenshotItemsL(); + + // Next clean the excess special cases + iCleanupState = ECleaningHangingSpecialCases; + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CleanSpecialHangingsL() + { + DLTRACEIN(("")); + + if ( iDbMetaIdentifiers.Count() > 0 ) + { + DLINFO(("More metas left")); + + // There are still some metadatas. + // So, check their special contents. + CNcdNodeMetaData* metaData( NULL ); + HBufC8* data( NULL ); + CNcdNodeIdentifier* metaDataIdentifier( iDbMetaIdentifiers[ 0 ] ); + + // The read operation may leave for example with KErrNotFound if the + // item was not found. Ignore the error and just continue normally. + TRAP_IGNORE( + data = + NodeDbManager(). + ReadDataFromDatabaseL( *metaDataIdentifier, + NcdNodeClassIds::ENcdMetaData ) ); + + if ( data != NULL ) + { + CleanupStack::PushL( data ); + if ( *data != KNullDesC8 ) + { + TRAP_IGNORE( + metaData = + NodeFactory().CreateMetaDataL( *metaDataIdentifier, + *data ) ); + if ( metaData != NULL ) + { + DLINFO(("Metadata was found")); + + // Notice that the metadata should be closed not deleted! + CleanupClosePushL( *metaData ); + + // Now that we have the metadata. + // Check the icons + CNcdNodeIcon* icon( NULL ); + TRAP_IGNORE( icon = &metaData->IconL() ); + if ( icon != NULL ) + { + DLINFO(("icon check")); + // Check if the icon can be found from the icon list + // If its found then remove it from the list. + CNcdNodeIdentifier* iconIdentifier( NULL ); + for ( TInt i = iAllDbIconItems.Count(); i > 0; --i ) + { + iconIdentifier = iAllDbIconItems[ i - 1 ]; + if ( icon->IconId() == iconIdentifier->NodeId() ) + { + // Icon was found. + DLINFO((_L("Icon found and no need to remove: %S"), + &iconIdentifier->NodeId())); + delete iAllDbIconItems[ i - 1 ]; + iAllDbIconItems.Remove( i - 1 ); + } + } + } + + + const CNcdNodeScreenshot* screenshot( NULL ); + TRAP_IGNORE( screenshot = &metaData->ScreenshotL() ); + if ( screenshot != NULL ) + { + DLINFO(("screenshot check")); + // Check if the screenshot can be found from the icon list + // If its found then remove it from the list. + CNcdNodeIdentifier* screenshotIdentifier( NULL ); + for ( TInt i = iAllDbScreenshotItems.Count(); i > 0; --i ) + { + screenshotIdentifier = iAllDbScreenshotItems[ i - 1 ]; + TInt shotCount = screenshot->ScreenshotDownloadCount(); + DLTRACE(("Going through %d screenshots", shotCount )); + while ( shotCount-- ) + { + if ( screenshot->ScreenshotDownloadUri( shotCount ) + == screenshotIdentifier->NodeId() ) + { + // Screenshot was found. + DLINFO(("screenshot found")); + delete iAllDbScreenshotItems[ i - 1 ]; + iAllDbScreenshotItems.Remove( i - 1 ); + } + } + } + } + + // When metadata is deleted, also icons, + // etc. are deleted. + CleanupStack::PopAndDestroy( metaData ); + } + } + CleanupStack::PopAndDestroy( data ); + } + + // Remove the used identifier. + // So, next round will have a new identifier in the beginning of the array + delete iDbMetaIdentifiers[ 0 ]; + iDbMetaIdentifiers.Remove( 0 ); + + // Continue cleaning special cases + iCleanupState = ECleaningHangingSpecialCases; + } + else + { + // All the metadatas have been gone through. + // So, the special arrays will contain + // identifiers of the items that should be removed + // from the db. + // So, go to the next level. + iCleanupState = EFinishCleaningHangingSpecialCases; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::FinishCleanSpecialHangingsL() + { + DLTRACEIN(("")); + + // Special arrays contain only the identifiers that + // belong to no metadata. + // So, delete those icons. + + DLINFO(("Remove icons from db: %d", iAllDbIconItems.Count())); + // Print array debug info into the log file + #ifdef NCD_NODE_CACHE_CLEANER_DEBUG_ARRAY_PRINT + DLINFO(("iAllDbIconItems array:")); + ArrayDebugPrint( iAllDbIconItems ); + #endif + + NodeDbManager(). + RemoveDataFromDatabaseL( iAllDbIconItems, + iIconCleanupTypes ); + iAllDbIconItems.ResetAndDestroy(); + + + DLINFO(("Remove screenshot from db: %d", iAllDbScreenshotItems.Count() )); + NodeDbManager(). + RemoveDataFromDatabaseL( iAllDbScreenshotItems, + iScreenshotCleanupTypes ); + iAllDbScreenshotItems.ResetAndDestroy(); + + // Everything is done here. + // So, go to the next level. + if ( iExcessCleanupStarted ) + { + // Because excess cleanup has already been started we have looped here back. + // So, skip the start and go directly to the correct cleaning procedure. + // Notice, that this also skips the expireds check, which is what we want + // because expired should be checked only once to save some time. If for some + // reason some nodes will expire during this cleanup it does not matter. + // Also, transparency and bundle checks are skipped, because they will be + // handled already during the first loop. + // They will be cleaned another time. + iCleanupState = ECleaningExcess; + } + else + { + // Start cleaning. + iCleanupState = EStartCleaningExcess; + } + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::StartCleanExcessL() + { + DLTRACEIN(("")); + + TInt currentDbSize( + NodeDbManager().StorageSizeL( ClientUid(), + *iDoNotCleanNameSpaces ) ); + TInt compareSize( DbMaxSize() ); + + DLINFO(("current db size: %d, compare size: %d", + currentDbSize, compareSize)); + + TBool stopCleaning( ETrue ); + if ( compareSize < currentDbSize ) + { + // Get the current node items from the db. These will be gone through + // to decide what should be removed if the current database size is too + // big. + // This is the last change to update the list before the excess cleaning + // starts to loop. Then the node list should not be updated because + // that might result to a infinite loop, if deepest nodes can not be removed. + // We will not come back here if excess cleaning is looping. + // Notice that actually it does not matter if not all the items can not be + // checked here. They will be checked next time the cleaning is started. + SetAllDbStorageNodeItemsL(); + if ( iAllDbStorageNodeItems.Count() > 0 ) + { + // We still have to cleanup some more. + // So, make new request for the next step. + stopCleaning = EFalse; + iExcessCleanupStarted = ETrue; + + // Next check the root children, transparent and bundle nodes + // and after that expired nodes before actually removing the + // deepest nodes. + iCleanupState = EStartRootChildrenCheck; + } + } + + // Nothing to clean or the database size is small enough. + if ( stopCleaning ) + { + DLINFO(("Excess cleaned")); + // Just in case somebody has added something for removal. + // This call will update the node list once more from db + // and reset it when cleaning is done. The array update is ok + // here, because no additional loop will be done. + HandleCleaningL( ETrue, ETrue ); + // Nothing to do next + iCleanupState = EStopping; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::StartRootChildrenCheckL() + { + DLTRACEIN(("")); + + // Bundles are always direct children of the root. + // Transparent folders are always direct children of the root + // or the bundles. + // So, start the checking from the root node and its children. + + CNcdNodeIdentifier* rootIdentifier = + NcdNodeIdentifierEditor::CreateRootIdentifierForClientLC( ClientUid() ); + + // As a default, make the next step to be cleaning expireds. + // This will skip the transparent and bundle checks. + // If the root is found below, then the state will be changed to the + // required value. + iCleanupState = ECleaningExpireds; + + // Here, we remove the root from the db node list if it is included there. + // The root should not be removed from the db later anyway. + // So, we may as well remove it from the db node list now. + iRootChildren.ResetAndDestroy(); + if ( RemoveIdentifier( *rootIdentifier, iAllDbStorageNodeItems ) ) + { + // Root was in the list. So, it can be found from db. + // Thus, we can try to create the root node here. + // Also, check the root children. + CNcdRootNode* root( NULL ); + CNcdNode* node( NULL ); + HBufC8* nodeData( NULL ); + + // The read operation may leave for example with KErrNotFound if the + // item was not found. Ignore the error and just continue normally. + TRAP_IGNORE( + nodeData = + NodeDbManager(). + ReadDataFromDatabaseL( *rootIdentifier, + NcdNodeClassIds::ENcdNode ) ); + if ( nodeData != NULL ) + { + CleanupStack::PushL( nodeData ); + if ( *nodeData != KNullDesC8 ) + { + TRAP_IGNORE( + node = + NodeFactory().CreateNodeL( *rootIdentifier, + *nodeData ) ); + + if ( node != NULL ) + { + // Push the node into the cleanup stack because checking + // may leave. + CleanupClosePushL( *node ); + if ( CNcdNodeFactory::NodeTypeL( *node ) + == CNcdNodeFactory::ENcdNodeRoot ) + { + // Only handle the root. + root = static_cast( node ); + // Release the node from the cleanupstack. + // It will be put back as a root next. + CleanupStack::Pop( node ); + } + else + { + DLERROR(("The node with root identifier was not root.")) + DASSERT( EFalse ); + // Delete the node because it was not of the right type. + // The root will be left to NULL. + CleanupStack::PopAndDestroy( node ); + node = NULL; + } + } + + if ( root != NULL ) + { + // Notice that the node should be closed not deleted! + CleanupClosePushL( *root ); + const RPointerArray& children = root->ChildArray(); + for ( TInt i = 0; i < children.Count(); i++ ) + { + // Because the root child will be added into the root list + // and those children are handled separately, remove + // the children also from the original node list. + // When iAllDbStorageNodeItems was initialized it contained all the + // identifiers of all the nodes in db. + // Notice, that if the children are not in the db node list, then + // they will not be removed from the db here. + + // So, check if the identifier is found from the db list and remove it + // if found. + if ( RemoveIdentifier( children[i]->Identifier(), iAllDbStorageNodeItems ) ) + { + // The root child was in the db list and it was removed. + // Now, insert the identifier into the temporary root list. + // So, these children will be checked later for transparency and + // bundle features. + AddIdentifierL( children[i]->Identifier(), iRootChildren ); + } + } + CleanupStack::PopAndDestroy( root ); + + // Because we got the root, then next step is to handle its + // children. + iCleanupState = ECheckRootChildren; + } + } + + // Delete node data. + CleanupStack::PopAndDestroy( nodeData ); + } + } + + CleanupStack::PopAndDestroy( rootIdentifier ); + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CheckRootChildrenL() + { + DLTRACEIN(("")); + + if ( iRootChildren.Count() > 0 ) + { + DLINFO(("Root children in the array.")); + + // As a default loop here after this stuff is done. + // This way next loop will check if there are still more children + // to be checked. + iCleanupState = ECheckRootChildren; + + // All the children of the root should always be folders. + CNcdNode* node( NULL ); + CNcdNodeFolder* folder( NULL ); + HBufC8* nodeData( NULL ); + CNcdNodeIdentifier* rootChildIdentifier( iRootChildren[ 0 ] ); + + // The read operation may leave for example with KErrNotFound if the + // item was not found. Ignore the error and just continue normally. + TRAP_IGNORE( + nodeData = + NodeDbManager(). + ReadDataFromDatabaseL( *rootChildIdentifier, + NcdNodeClassIds::ENcdNode ) ); + if ( nodeData != NULL ) + { + CleanupStack::PushL( nodeData ); + if ( *nodeData != KNullDesC8 ) + { + TRAP_IGNORE( + node = + NodeFactory().CreateNodeL( *rootChildIdentifier, + *nodeData ) ); + if ( node != NULL ) + { + // Push the node into the cleanup stack because checking + // may leave. + CleanupClosePushL( *node ); + if ( CNcdNodeFactory::NodeTypeL( *node ) + == CNcdNodeFactory::ENcdNodeFolder ) + { + // Only handle the folders here. + // Else, the child can not be bundle or transparent. + folder = static_cast( node ); + // Remove the node from the cleanup stack but do not delete + // it here. It will be used as a folder next. + CleanupStack::Pop( node ); + } + else + { + // Delete the node because it was not of the right type. + DLERROR(("The root child was not a folder. Has the specification been changed?")); + DASSERT( EFalse ); + CleanupStack::PopAndDestroy( node ); + } + } + + if ( folder != NULL ) + { + // Notice that the node should be closed not deleted! + CleanupClosePushL( *folder ); + CNcdNodeFactory::TNcdNodePurpose nodePurpose( + CNcdNodeFactory::NodePurposeL( *folder ) ); + + if ( nodePurpose == CNcdNodeFactory::ENcdTransparentNode ) + { + DLINFO(("Transparent folder.")); + const RPointerArray& children = folder->ChildArray(); + for ( TInt i = 0; i < children.Count(); i++ ) + { + // Because the folder is transparent folder, + // remove the child identifiers from the node list. + // So, those transparent children will not be + // removed from the database later. + + // Notice, that if the children of the transparent folder + // are not in the db list, then they will not be removed + // from the db. + RemoveIdentifier( children[i]->Identifier(), + iAllDbStorageNodeItems ); + } + } + else if ( nodePurpose == CNcdNodeFactory::ENcdBundleNode ) + { + DLINFO(("Bundle folder")) + // Next, we have to handle the bundle children and check + // if they are transparent + iBundleChildren.ResetAndDestroy(); + const RPointerArray& children = folder->ChildArray(); + for ( TInt i = 0; i < children.Count(); i++ ) + { + if ( RemoveIdentifier( children[i]->Identifier(), + iAllDbStorageNodeItems ) ) + { + // The identifier was found from the db list and removed. + // So, because the node can be found from the db, it can be + // created. So, add it into the bundle children temporary list. + AddIdentifierL( children[i]->Identifier(), iBundleChildren ); + } + } + + // Now that we got the children of the bundle folder, + // we should handle them separately during next active rounds. + iCleanupState = EHandleBundleChildren; + } + CleanupStack::PopAndDestroy( folder ); + } + } + + // Delete node data. + CleanupStack::PopAndDestroy( nodeData ); + } + + // Now, the root child has been checked. So, remove its identifier + // from the root array. + delete iRootChildren[ 0 ]; + iRootChildren.Remove( 0 ); + } + else + { + // No more children to be checked. + // So, skip to expireds. + iCleanupState = ECleaningExpireds; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::HandleBundleChildrenL() + { + DLTRACEIN(("")); + + // Check all the bundle children for transparent folders. + + if ( iBundleChildren.Count() > 0 ) + { + DLINFO(("Bundle children in the array.")); + + // As a default loop here after this stuff is done. + // This way next loop will check if there are still more children + // to be checked. + iCleanupState = EHandleBundleChildren; + + // All the children of the root should always be folders. + CNcdNode* node( NULL ); + CNcdNodeFolder* folder( NULL ); + HBufC8* nodeData( NULL ); + CNcdNodeIdentifier* bundleChildIdentifier( iBundleChildren[ 0 ] ); + + // The read operation may leave for example with KErrNotFound if the + // item was not found. Ignore the error and just continue normally. + TRAP_IGNORE( + nodeData = + NodeDbManager(). + ReadDataFromDatabaseL( *bundleChildIdentifier, + NcdNodeClassIds::ENcdNode ) ); + if ( nodeData != NULL ) + { + CleanupStack::PushL( nodeData ); + if ( *nodeData != KNullDesC8 ) + { + TRAP_IGNORE( + node = + NodeFactory().CreateNodeL( *bundleChildIdentifier, + *nodeData ) ); + if ( node != NULL ) + { + // Push the node into the cleanup stack because checking + // may leave. + CleanupClosePushL( *node ); + if ( CNcdNodeFactory::NodeTypeL( *node ) + == CNcdNodeFactory::ENcdNodeFolder ) + { + // Only handle the folders here. + // Else, the child can not be transparent. + folder = static_cast( node ); + } + // Remove the node from the cleanup stack but do not delete + // it here. It will be used as a folder next. + CleanupStack::Pop( node ); + } + + if ( folder != NULL ) + { + // Notice that the node should be closed not deleted! + CleanupClosePushL( *folder ); + CNcdNodeFactory::TNcdNodePurpose nodePurpose( + CNcdNodeFactory::NodePurposeL( *folder ) ); + + if ( nodePurpose == CNcdNodeFactory::ENcdTransparentNode ) + { + DLINFO(("Transparent folder.")); + const RPointerArray& children = folder->ChildArray(); + for ( TInt i = 0; i < children.Count(); ++i ) + { + // Because the folder is transparent folder, + // remove the child identifiers from the node list. + // So, those transparent children will not be + // removed from the database. + + // Because the root child will be added into the root list + // and those children are handled separately, remove + // the children also from the original node list. + // Notice, that if the children are not in the db list, then + // there will not be any need to remove them from the db either. + // So, check it here. + RemoveIdentifier( children[i]->Identifier(), + iAllDbStorageNodeItems ); + } + } + CleanupStack::PopAndDestroy( folder ); + } + } + + // Delete node data. + CleanupStack::PopAndDestroy( nodeData ); + } + + // Now the root child has been checked. So, remove its identifier + // from the root array. + delete iBundleChildren[ 0 ]; + iBundleChildren.Remove( 0 ); + } + else + { + // No more bundle children to be checked. + // So, skip back to root child checking. + iCleanupState = ECheckRootChildren; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CleanExpiredsL() + { + DLTRACEIN(("")); + + // Notice that this function is looped if there are multiple nodes in the + // iAllDbStorageNodeItems. The array may not be uptodate if some nodes are + // inserted into the database during this active object looping. This does + // not matter because then only those new items will not be checked here. + // They will be checked when the cleaner is started next time. The array + // could be updated here every time but because db-actions may take time + // this is omitted here. + + // Notice that because the db items are sorted when the array is created, + // the removing of the children from the end list does not affect this index. + // So, if the current item is removed then also its children are removed, but + // the children exist after the parent in the array. + if ( iExpiredCleaningIndex < iAllDbStorageNodeItems.Count() ) + { + CNcdNodeIdentifier* itemIdentifier( + iAllDbStorageNodeItems[ iExpiredCleaningIndex ] ); + + DLINFO((_L("Cleaning index: %d, id: %S"), + iExpiredCleaningIndex, &itemIdentifier->NodeId())); + + // Set the expire to be true by default. So, if node can not be initialized + // correctly it will be removed also in that case. + TBool expired( ETrue ); + TInt depth( NcdNodeIdentifierEditor::NodeDepthL( *itemIdentifier ) ); + + if ( depth < 2 ) + { + DLINFO(("No need to check expiration. Depth: %d", depth)); + // Root nodes are not removed here. They will be updated by the + // other methods when they expire. + // Only items that are not root or direct children of root should + // be removed here. Notice that temporary items have depth zero, but + // their are cleaned during temporary cleaning. So, omitting them here + // is ok. + // Notice also, that items directly under the root should not be + // removed by the cleaner. For example, bundle folders should be + // updated only same time as root, because their info is only gotten + // during the root refresh. + expired = EFalse; + } + else + { + // Node will not be some root so, check the expiration here. + HBufC8* nodeData( NULL ); + + // The read operation may leave for example with KErrNotFound if the + // item was not found. Ignore the error and just continue normally. + TRAP_IGNORE( + nodeData = + NodeDbManager(). + ReadDataFromDatabaseL( *itemIdentifier, + NcdNodeClassIds::ENcdNode ) ); + if ( nodeData != NULL ) + { + if ( *nodeData != KNullDesC8 ) + { + CNcdNode* node( NULL ); + TRAP_IGNORE( + node = + NodeFactory().CreateNodeL( *itemIdentifier, + *nodeData ) ); + if ( node != NULL ) + { + // Notice that the node should be closed not deleted! + CNcdNodeLink* link = node->NodeLink(); + if ( link ) + { + // Check if the node should be marked not expired. + expired = link->IsExpired(); + } + node->Close(); + } + } + + // Delete node data. + delete nodeData; + } + } + + if ( expired ) + { + DLINFO((_L("Expired: %S"), &itemIdentifier->NodeId())); + // Because the node was expired, or it could not be created for some reason + // correctly. Add the identifier to the cleanup list if it is allowed action. + // This function knows how to move the item into the waiting list. + AddCleanupIdentifierL( *itemIdentifier ); + + // Remove the data from its original place. Notice that identifier + // is copied in the function above. + delete itemIdentifier; + iAllDbStorageNodeItems.Remove( iExpiredCleaningIndex ); + + // HandleCleaningL and the functions it uses can handle the removing of the + // item from the db array. + HandleCleaningL( EFalse, EFalse ); + } + else + { + // Increase the cleaning index by one for the next round + // because the current item was not removed. + ++iExpiredCleaningIndex; + } + + // There may still be something to be done. + // So, make new request for the next step. + iCleanupState = ECleaningExpireds; + } + else + { + DLINFO(("Expireds cleaned")); + // Reset the cleaning index. + iExpiredCleaningIndex = 0; + + // Because expireds are cleaned now. Check if the excess nodes should + // still be removed from the database to free enough space. + iCleanupState = ECleaningExcess; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::CleanExcessL() + { + DLTRACEIN(("")); + + TInt currentDbSize( + NodeDbManager().StorageSizeL( ClientUid(), + *iDoNotCleanNameSpaces ) ); + + // Because cleaning has been started the compare size should be somewhat + // less than the max accepted size after the cleaning operation. + TInt compareSize( DbMaxSize() / KMaxSizeDivider ); + + DLINFO(("current db size: %d, compare size: %d", + currentDbSize, compareSize)); + + // Here the node list may not be uptodate if database has changed during the + // active loops. But do not update it, because otherwise we might try to remove + // same node over and over again. If the deepest nodes are not allowed to be + // removed. + TInt itemCount( iAllDbStorageNodeItems.Count() ); + TInt deepestDepth( 0 ); + + if ( itemCount > 0 ) + { + // Get the depth of the final node in db list. This is the deepest node because + // the array has been sorted. + deepestDepth = + NcdNodeIdentifierEditor::NodeDepthL( *iAllDbStorageNodeItems[ itemCount - 1 ] ); + if ( deepestDepth < 2 ) + { + DLINFO(("Do not clean excess any more. Depth: %d", deepestDepth)); + // Because we should not delete root or its direct children here. + // Reset the node items array and reset the item count. + // Because the item count will be zero now, + // the cleaning will continue to the stopping state next. + iAllDbStorageNodeItems.ResetAndDestroy(); + itemCount = iAllDbStorageNodeItems.Count(); + } + } + + // Start actual cleaning if it is still required. + if ( itemCount > 0 + && compareSize < currentDbSize ) + { + DLINFO(("Excess cleaning is required")); + + DLINFO(("Deepest depth: %d", deepestDepth)); + + CNcdNodeIdentifier* identifier( NULL ); + for ( TInt i = itemCount; i > 0; --i ) + { + identifier = iAllDbStorageNodeItems[ i - 1 ]; + if ( deepestDepth != NcdNodeIdentifierEditor::NodeDepthL( *identifier ) ) + { + // Only remove items that have the same depth at once. + DLINFO(("All the same depth nodes added.")); + break; + } + + // This identifier has also the same depth. + // Insert it into the cleanup array if removal is allowed. + AddCleanupIdentifierL( *iAllDbStorageNodeItems[ i - 1 ] ); + + // Remove the data from its original place. Notice that identifier + // is copied in the function above. + delete iAllDbStorageNodeItems[ i - 1 ]; + iAllDbStorageNodeItems.Remove( i - 1 ); + + DLINFO(("Same depth id added to array")); + } + + // Clean the nodes from the database. + HandleCleaningL( EFalse, EFalse ); + + // We may still have to cleanup some more. + // So, make new request for the next step. + // This will clean the metadata related data such as icons. + // The cleaning will loop until enough space is released. + iCleanupState = ECleaningTemporaryNodes; + } + else + { + DLINFO(("Excess cleaned")); + // Just in case somebody has added something for removal + // while waiting this new round. + // Because we are about to end the cleaning we can update the + // node list one more time before final removing. + HandleCleaningL( ETrue, ETrue ); + + // Nothing to do next + iCleanupState = EStopping; + } + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* ptrStatus = &iStatus; + User::RequestComplete( ptrStatus, KErrNone ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeCacheCleaner::StoppingL() + { + DLTRACEIN(("")); + + // This function resets the member variables to default values. + // Also, the priority of this active object is set to the default. + ResetState(); + + DLTRACEOUT(("")); + } +