diff -r 000000000000 -r ba25891c3a9e iaupdate/IAD/engine/controller/src/iaupdatenodecontainer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iaupdate/IAD/engine/controller/src/iaupdatenodecontainer.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,1089 @@ +/* +* Copyright (c) 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: ?Description +* +*/ + + + +#include "iaupdatenodecontainer.h" + +#include "iaupdatecontrollerimpl.h" +#include "iaupdatecontentoperationmanager.h" +#include "iaupdatenodeimpl.h" +#include "iaupdatefwnodeimpl.h" +#include "iaupdatenodedetails.h" +#include "iaupdatenodedependencyimpl.h" +#include "iaupdateplatformdependency.h" +#include "iaupdateutils.h" +#include "iaupdatenodefactory.h" + +#include "iaupdatedebug.h" + + +// Constant that is used to inform that dependency node +// was not found when dependencies were checked. +const TInt KDependencyNotFound( -1 ); + + +CIAUpdateNodeContainer* CIAUpdateNodeContainer::NewLC( + CIAUpdateController& aController ) + { + CIAUpdateNodeContainer *self = + new (ELeave) CIAUpdateNodeContainer( aController ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + +CIAUpdateNodeContainer* CIAUpdateNodeContainer::NewL( + CIAUpdateController& aController ) + { + CIAUpdateNodeContainer *self = + CIAUpdateNodeContainer::NewLC( aController ); + CleanupStack::Pop( self ); + return self; + } + + +CIAUpdateNodeContainer::CIAUpdateNodeContainer( + CIAUpdateController& aController ) +: CBase(), + iController( aController ) + { + } + +void CIAUpdateNodeContainer::ConstructL() + { + } + + +CIAUpdateNodeContainer::~CIAUpdateNodeContainer() + { + Clear(); + } + + +void CIAUpdateNodeContainer::AddNodeL( CIAUpdateNode* aNode ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddNodeL() begin"); + + if ( !aNode ) + { + // Null is not acceptable + IAUPDATE_TRACE("[IAUPDATE] LEAVE: Node null"); + User::Leave( KErrArgument ); + } + + CleanupStack::PushL( aNode ); + + IAUPDATE_TRACE("[IAUPDATE] Check if node is acceptable"); + if ( !NodeAlreadyExists( *aNode ) + && aNode->Details().PlatformDependency().AcceptablePlatformL() + && InstallCheckL( *aNode ) + && !aNode->Details().EmbededDegrades() + && PackageTypeAcceptedL( *aNode ) ) + { + IAUPDATE_TRACE("[IAUPDATE] Node accepted. Add into the node array."); + + // The node list will contain nodes + // that can be downloaded and installed. + // The header node list will provide + // most recent version of the content. + + // The ownership of the node transfers. + // So try to append it into the array. + // If node can not be added into the array, + // it will be deleted when appending leaves. + iNodes.AppendL( aNode ); + // Appending was successfull. + // So, ownership has been transferred successfully. + CleanupStack::Pop( aNode ); + } + else + { + IAUPDATE_TRACE("[IAUPDATE] Node not accepted. Delete it."); + + // Because the node is not deployable + // or it was for the wrong platform, + // there is no use for it. Delete the node. + CleanupStack::PopAndDestroy( aNode ); + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddNodeL() end"); + } + + +void CIAUpdateNodeContainer::AddExcessNodeL( CIAUpdateNode* aNode ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddExcessNodeL() begin"); + + if ( aNode == NULL ) + { + // Null is not acceptable + IAUPDATE_TRACE("[IAUPDATE] LEAVE: NULL node"); + User::Leave( KErrArgument ); + } + + // The node list will contain nodes that do not belong to any other + // node category. + + // The ownership of the node transfers. + // So try to append it into the array. + CleanupStack::PushL( aNode ); + iExcessNodes.AppendL( aNode ); + CleanupStack::Pop( aNode ); + + // Force the node to be hidden. + aNode->ForceHidden( ETrue ); + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddExcessNodeL() end"); + } + + +void CIAUpdateNodeContainer::AddFwNodeL( CIAUpdateFwNode* aNode ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddFwNodeL() begin"); + + if ( !aNode ) + { + // Null is not acceptable + IAUPDATE_TRACE("[IAUPDATE] LEAVE: Node null"); + User::Leave( KErrArgument ); + } + + CleanupStack::PushL( aNode ); + + iFwNodes.AppendL( aNode ); + + CleanupStack::Pop( aNode ); + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::AddFwNodeL() end"); + } + + +void CIAUpdateNodeContainer::Clear() + { + // Just reset this list because all the nodes + // are owned by the iNodes list. + iHeadNodes.Reset(); + + // Reset the list and delete the nodes. + iNodes.ResetAndDestroy(); + + // Reset the list and delete the nodes. + iExcessNodes.ResetAndDestroy(); + + // Reset the list and delete the nodes. + iFwNodes.ResetAndDestroy(); + } + + +const RPointerArray< CIAUpdateNode >& CIAUpdateNodeContainer::AllNodes() const + { + return iNodes; + } + + +const RPointerArray< CIAUpdateNode >& CIAUpdateNodeContainer::ExcessNodes() const + { + return iExcessNodes; + } + + +const RPointerArray< CIAUpdateNode >& CIAUpdateNodeContainer::HeadNodesL() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::HeadNodesL() begin"); + + // Make sure that dependencies are correct. + // Notice, that here the dependencies are checked for all the nodes + // in the node list. So, some dependencies that may not be required + // are also checked. (Unnecessary dependency checks can exist, for example, + // if the server sends multiple update packets for a same content version.) + // But, checking of dependencies first makes it easier to find correct header + // nodes later because we can be sure that all the nodes have intact dependency + // chains then. Also, notice that this function will set the information about + // the dependency checks into the nodes and remove nodes, whose dependencies + // are broken, from the list. + UpdateDependenciesL(); + + // Create the header node list and remove nodes + // whose dependency chains are broken. + ManageHeaderNodesL(); + + // Create the dependecy for self update nodes + // and mark selfupdater and ncd hidden. + // RemoveHiddenNodesFromHeadList will handle those nodes + // after they have been marked hidden. + CreateSelfUpdateBundleL(); + + // This will set the service pack dependency nodes as hidden. + HandleServicePacksL(); + + // Bundles may contain nodes that are head nodes + // but have been marked hidden. + // So, remove hidden nodes from the head list. + // Hidden nodes should be visible to UI. + RemoveHiddenNodesFromHeadList(); + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::HeadNodesL() end"); + + return iHeadNodes; + } + + +const RPointerArray< CIAUpdateFwNode >& CIAUpdateNodeContainer::FwNodes() + { + return iFwNodes; + } + + +void CIAUpdateNodeContainer::UpdateDependenciesL() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateDependenciesL() begin"); + + // Reset node statuses + for ( TInt i = 0; i < iNodes.Count(); ++i ) + { + iNodes[ i ]->Reset(); + } + + // Check and update the dependencies for all the nodes. + // So, it will be checked that all the nodes can be installed correctly + // and no dependencies for them are missing. + for ( TInt i = 0; i < iNodes.Count(); ++i ) + { + // If the node dependency status has already been set, + // then it was already checked in some previous loop + // or recursion of dependency check. So, do not recheck + // the same branch here. Notice, the DependencyCheckStatus is + // also checked in the recursion in UpdateNodeDependenciesL. + // So, recheck should not even be allowed here. Otherwise checks + // will fail unless the statuses are resetted before. + // If the state has not been set, then check the branch. + CIAUpdateNode& node( *iNodes[ i ] ); + if ( node.DependencyCheckStatus() + == CIAUpdateNode::EDependencyCheckNotSet ) + { + // Notice, that this function sets the dependency check status + // for the node and to the nodes belonging to that branch in + // its dependency tree. + UpdateNodeDependenciesL( node, 0 ); + } + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateDependenciesL() end"); + } + + +TInt CIAUpdateNodeContainer::UpdateNodeDependenciesL( + CIAUpdateNode& aNode, TInt aDepth ) + { + IAUPDATE_TRACE_2("[IAUPDATE] CIAUpdateNodeContainer::UpdateNodeDependenciesL() begin: %S, %S", + &aNode.MetaNamespace(), &aNode.MetaId()); + + // Notice that this function creates a recursive loop together with + // FindBestMatchL function. + + // Check if we have already handled this node and dependency loops back to it. + // If node check is already going on for the node, then there is loop in dependency. + // Do not accept loops for dependencies. If no loop, then continue normally. + // Notice, that if we loop here, other sub branches may have not been + // checked for this node yet. So, only set the status when the whole subtree has + // been checked. + + if ( aNode.DependencyCheckStatus() + == CIAUpdateNode::EDependencyCheckNotSet ) + { + IAUPDATE_TRACE("[IAUPDATE] Dependency check not set yet"); + + // Gives the total leaf distance of this part of the dependency tree. + // The tallest branch gives the total leaf distance. + TInt totalLeafDistance( KDependencyNotFound ); + + // Mark the check state here to prevent loops. + aNode.SetDependencyCheckStatus( CIAUpdateNode::EDependencyCheckGoing ); + + RPointerArray< CIAUpdateNodeDependency > deps; + CleanupClosePushL( deps ); + + // Get dependency objects from aNode into the deps array. + // Get dependencies that were gotten from the server. + aNode.Details().GetDependenciesL( deps ); + + // Now, that we have the dependency information for the given node, + // find the nodes that provide the best dependency branches. + for ( TInt i = 0; i < deps.Count(); ++i ) + { + // The best branch is thought to be the branch that is the flattest. + // FindBestMatchL function inserts the best match information + // to the dependency object. The best match information of the dependency + // will contain the possible node whose content is required + // for the dependency chain to be complete. Also, that node may need other nodes. + // So, the dependency branch may continue there. + // Dependency depth is one greater than the depth of dependant aNode. + TInt leafDistance( + FindBestMatchL( aNode, *deps[ i ], aDepth + 1 ) ); + + // Notice, that here we are looking for the total leaf distance for this branch. + // FindBestMatchL -function that is used above will use this value to check + // what branch has the shortes totalLeafDistance. + // The branch that is flattest will be chosen for the current parent node. + if ( leafDistance == KDependencyNotFound ) + { + IAUPDATE_TRACE("[IAUPDATE] Dependency check failed"); + + // Because dependency was not found from the node list or from + // the installed applications, the node dependency chain is broken. + // Set this information into the node. + // So, this node will be removed from the node list later. + aNode.SetDependencyCheckStatus( CIAUpdateNode::EDependencyCheckFailed ); + + // Because, one missing item is enough to break the chain, just return here. + // If other nodes are required, they are checked separately later in their own + // checkings. The chain break affects all the nodes that depend on this one. So, + // this information will be set to the nodes, when the recursion goes back. + CleanupStack::PopAndDestroy( &deps ); + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateNodeDependenciesL() end: KDependencyNotFound"); + + return KDependencyNotFound; + } + else if ( totalLeafDistance < leafDistance ) + { + IAUPDATE_TRACE_2("[IAUPDATE] Replace old totalLeafDistance %d, %d", + totalLeafDistance, leafDistance); + // Set the total leaf distance of this part of the dependency chain because + // the chain is intact. + totalLeafDistance = leafDistance; + } + } + + CleanupStack::PopAndDestroy( &deps ); + + // Dependency chain for the node is intact. + // Set this information into the node. + aNode.SetDependencyCheckStatus( CIAUpdateNode::EDependencyCheckPassed ); + IAUPDATE_TRACE("[IAUPDATE] EDependencyCheckPassed"); + + // Add one, so the parent will get its own actual leaf distance in return. + ++totalLeafDistance; + + // Also, set the total leaf distance for the node. + aNode.SetLeafDistance( totalLeafDistance ); + + + IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateNodeContainer::UpdateNodeDependenciesL() end: %d", + totalLeafDistance); + + return totalLeafDistance; + } + else if ( aNode.DependencyCheckStatus() + == CIAUpdateNode::EDependencyCheckPassed ) + { + IAUPDATE_TRACE("[IAUPDATE] Dependency check already passed"); + + // Because this node and the hierarchy below it has already been handled, + // check if the depth of the node and its dependency hierarchy should be + // updated with the new value. + aNode.UpdateDependencyDepthsL( aDepth ); + + // Because we have already handled this branch successfully, + // just return its own leaf distance information. + IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateNodeContainer::UpdateNodeDependenciesL() end: %d", + aNode.LeafDistance()); + return aNode.LeafDistance(); + } + else if ( aNode.DependencyCheckStatus() + == CIAUpdateNode::EDependencyCheckGoing ) + { + IAUPDATE_TRACE("[IAUPDATE] Dependency loop has occurred"); + // Notice, we come here if there are loops in dependencies. + // Looping dependency chains are not acceptable in normal cases. + // Notice, in case of service packs, if another branch of service pack + // is intact and it does not loop, then the service pack check status + // is set to CIAUpdateNode::EDependencyCheckPassed when the recursion + // is over above and all the branches of the service pack have been handled. + // Still loopin branches inside service pack will also be removed. + aNode.SetDependencyCheckStatus( CIAUpdateNode::EDependencyCheckFailed ); + } + + // We come here if dependency check status has been set to + // CIAUpdateNode::EDependencyCheckFailed. So, then always + // return KDependencyNotFound to correspond that situation. + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateNodeDependenciesL() end: KDependencyNotFound"); + + return KDependencyNotFound; + } + + +TInt CIAUpdateNodeContainer::FindBestMatchL( CIAUpdateNode& aNode, + CIAUpdateNodeDependency& aDependency, + TInt aDependencyDepth ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::FindBestMatchL() begin"); + + // Notice that this function creates a recursive loop together with + // UpdateNodeDependenciesL function. + + // As a default, the dependency we are looking for is marked as not found. + TInt totalLeafDistance( KDependencyNotFound ); + + // Check if the dependency file has already been installed and get the version + // of the application if it has been installed. + TIAUpdateVersion installedVersion; + TBool installed( + IAUpdateUtils::IsAppInstalledL( + aDependency.Uid(), installedVersion ) ); + + if ( aDependency.IsEmbedded() ) + { + IAUPDATE_TRACE("[IAUPDATE] Dependency is embedded in the node."); + // If dependency node is already embedded into the package, then use it + // instead of downloading it separately. Embedded dependencies are always + // matched by the node itself. Embedded package has to be installed + // when content is installed, or the whole installation fails. + // Best match for this dependency is the node content itself. But, + // to prevent loops, set the best match to NULL. + aDependency.SetBestMatch( NULL ); + + // If the embedded node will require downgrades, + // then do not accept this dependency. Notice, here we think + // that the embedded content version value is given as version roof value. + // So, the installed version has to be equal or smaller than the roof value, + // if the embedded content can be accepted. + // Note for future improvements: + // When support is added for embedded sisx to have dependencies, + // this needs to be changed accordingly + if ( !installed + || installedVersion <= aDependency.VersionRoof() ) + { + IAUPDATE_TRACE("[IAUPDATE] Embedded content can be installed"); + // Leaf distance is zero because embedded item is already part + // of the node itself. + totalLeafDistance = 0; + } + } + else + { + IAUPDATE_TRACE("[IAUPDATE] Dependency content is not embedded."); + + // Check here if the best matching content is already installed or if + // content needs to be downloaded and installed + + // Note, that the dependency may also describe an update dependency + // for the content instead of just describing that a content depends + // on another content. But, we can handle it all these cases in a same + // way. + + // This value will be updated when the recursive call of + // UpdateNodeDependenciesL inside for-loop has found an acceptable leaf node. + // Then, recursion will come back of the tree hierarchy and find other branches + // and update this value if necessary. + CIAUpdateNode* currentBestMatch( NULL ); + + RPointerArray< CIAUpdateNode > matches; + CleanupClosePushL( matches ); + + // Find the nodes that fullfill the given dependency requirement. + // Node list may contain multiple alternatives. + // So, first find the alternatives. + // Notice, that this function skips already installed items. + FindMatchesL( matches, aDependency ); + + // Find the best match from the alternatives. + for ( TInt i = 0; i < matches.Count(); ++i ) + { + CIAUpdateNode& tmpNode( *matches[ i ] ); + + // The node that matches can be part of the best dependency branch. + // But, we still need to check the whole branch before we can be sure. + // So, check the dependency branch forward by using the recursion. + TInt leafDistance( + UpdateNodeDependenciesL( tmpNode, aDependencyDepth ) ); + + // If no match has been handled so far, then the new one is best match. + // If there is already a current best match, then check if the + // new one should replace it. + if ( leafDistance != KDependencyNotFound + && ( totalLeafDistance == KDependencyNotFound + || currentBestMatch + && ReplaceRecommendedL( + *currentBestMatch, tmpNode ) ) ) + { + IAUPDATE_TRACE("[IAUPDATE] New best match found."); + currentBestMatch = &tmpNode; + totalLeafDistance = leafDistance; + } + } + + CleanupStack::PopAndDestroy( &matches ); + + // Set the best match information for the dependency. + // Notice, that if installedVersion equals the currentBestMatch version, + // the node is set instead of NULL value. This way if a content in the + // middle of the dependency chain is installed, the dependency way will + // not be cut in that place. + if ( installed + && installedVersion >= aDependency.VersionFloor() + && installedVersion <= aDependency.VersionRoof() + && ( !currentBestMatch + || installedVersion > currentBestMatch->Version() ) ) + { + IAUPDATE_TRACE("[IAUPDATE] Best dependency content is already installed. Use it."); + // The content was already installed and its version belonged + // to the required version range. Also, the installed version + // was greater than the version of other matches. + // The best match was found, but it requires no nodes and their content. + aDependency.SetBestMatch( NULL ); + totalLeafDistance = 0; + } + else + { + IAUPDATE_TRACE("[IAUPDATE] Use current best match item if available"); + // If currentBestMatch is not NULL, the best dependency node was found. + // If currentBestMatch is NULL, the best dependency node was not found. + // So, just set the best match information accordingly. + aDependency.SetBestMatch( currentBestMatch ); + if ( currentBestMatch ) + { + IAUPDATE_TRACE("[IAUPDATE] Current best match item is available"); + + // Notice, that UpdateNodeDependenciesL checks and prevents looping. + // So, no need to do it here. AddDependant now, that we found the + // best match. Notice, we only want to insert dependant information + // to the correct item not to possible choices. So, that is why we + // do it here, not inside the for-loop above. + currentBestMatch->AddDependantL( aNode ); + + // Because node depth has not been set yet, update it now. + // Notice, we update the depth here because then the depth + // will be updated only to the best match node. In other words, + // the depth is updated only to the nodes that will be used. + // So, depths of other nodes that are skipped are not updated. + // This way the depth information will be correct if different + // nodes required alternative dependency hierarchies from some + // content. + currentBestMatch-> + UpdateDependencyDepthsL( aDependencyDepth ); + } + } + } + + IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateNodeContainer::FindBestMatchL() end: %d", + totalLeafDistance); + + return totalLeafDistance; + } + + +void CIAUpdateNodeContainer::FindMatchesL( RPointerArray< CIAUpdateNode >& aNodes, + const CIAUpdateNodeDependency& aDependency ) + { + // Find all the matching nodes from the node list. + for( TInt i = 0; i < iNodes.Count(); ++i ) + { + CIAUpdateNode& node( *iNodes[ i ] ); + // Check if the node matches the given dependency. + if( node.Uid() == aDependency.Uid() + && node.Version() >= aDependency.VersionFloor() + && node.Version() <= aDependency.VersionRoof() + && InstallCheckL( node ) ) + { + // Append the matching node into the given list. + aNodes.AppendL( &node ); + } + } + } + + +void CIAUpdateNodeContainer::ManageHeaderNodesL() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::ManageHeaderNodesL() begin"); + + for ( TInt i = iNodes.Count() - 1; i >= 0; --i ) + { + CIAUpdateNode& node( *iNodes[ i ] ); + if ( node.DependencyCheckStatus() + != CIAUpdateNode::EDependencyCheckPassed ) + { + // The node dependency chain has either failed + // or the nodes are not needed + // because their dependency status has not been set. + // So, remove these nodes from the node list. + IAUPDATE_TRACE_2("[IAUPDATE] Node dependency chain broken or node not needed: %S, %S", + &node.MetaNamespace(), &node.MetaId()); + delete iNodes[ i ]; + iNodes[ i ] = NULL; + iNodes.Remove( i ); + } + else + { + // Notice, that dependencies have been handled + // for every item previously. So, now we can choose + // the header nodes whose dependencies are intact. + // Update the header list to contain newer version + // of nodes whose contents have not been installed yet. + UpdateHeadNodeListL( node ); + } + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::ManageHeaderNodesL() end"); + } + + +void CIAUpdateNodeContainer::UpdateHeadNodeListL( CIAUpdateNode& aNode ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateHeadNodeListL() begin"); + + // All nodes that are not installed yet are possible head nodes. + // The final list will contain head nodes that are most recent + // versions of nodes that can be downloaded and/or installed. + + if( !aNode.IsInstalled() ) + { + IAUPDATE_TRACE("[IAUPDATE] Not installed yet."); + + // Only consider the highest version of nodes as head nodes. + + // This flag informs if the node should be appended into the list + // because it was not inserted in the for-loop + // or if the correct node was already in the list. + TBool alreadyIncluded( EFalse ); + + for ( TInt i = 0; i < iHeadNodes.Count(); ++i ) + { + CIAUpdateNode& tmpNode( *iHeadNodes[ i ] ); + if( aNode.Uid() == tmpNode.Uid() && aNode.Type() != MIAUpdateNode::EPackageTypeServicePack ) //MTA: Changed!!! + { + IAUPDATE_TRACE("[IAUPDATE] Head node already in the list."); + + // Node for the corresponding content already exists in the list. + // Replace node from the list if the new node is better choice. + if ( ReplaceRecommendedL( tmpNode, aNode ) ) + { + IAUPDATE_TRACE("[IAUPDATE] Replace existing head node"); + iHeadNodes[ i ] = &aNode; + // Because the old node was removed from the head list, + // it is not head node anymore. Set it as hidden. So, + // it will be handled correctly when downloading + // and installing is done. For example, if the new node + // depends on the older version. + tmpNode.ForceHidden( ETrue ); + } + else + { + IAUPDATE_TRACE("[IAUPDATE] Existing head node stays in head list"); + // The existing head node is newer. So, the recommended new node + // will not be head node. Make sure that it is set as hidden + // because hidden value is used to check if the node is head node + // or not. + aNode.ForceHidden( ETrue ); + } + + // We found a corresponding item from the head list. + // Now, the correct node is in the list. + alreadyIncluded = ETrue; + break; + } + } + + if( !alreadyIncluded ) + { + // The given node was not inserted above. + // So, append it now to the end of the list. + // Notice, that here we still may append hidden node into the head + // list. This way, the newest node is always in the head list when + // comparison is done. Later RemoveHiddenNodesFromHeadList + // is called. If the head node is hidden, it will be removed from + // the head list. This way we can be sure that head list contains + // only newest nodes and no visible head node version is older than + // some hidden one. + IAUPDATE_TRACE("[IAUPDATE] Add new head node into the list."); + iHeadNodes.AppendL( &aNode ); + } + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::UpdateHeadNodeListL() begin"); + } + + +TBool CIAUpdateNodeContainer::ReplaceRecommendedL( + const CIAUpdateNode& aCurrentNode, + const CIAUpdateNode& aNewNode ) const + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::ReplaceRecommendedL() begin"); + + if ( aCurrentNode.Uid() != aNewNode.Uid() ) + { + IAUPDATE_TRACE("[IAUPDATE] LEAVE: Nodes do not match."); + User::Leave( KErrArgument ); + } + + // New node is recommended if encountered node has higher version, + // or if the version is equal but current node packet type is + // not preferred. Also, the leaf distance of dependency chain is checked + // because long dependency chains should be avoided. + // The content package order is: (first) PU, SP, SA + // Notice, that PU and SP require already installed version of + // the application but this was already checked when node were added + // into the array. + if ( aNewNode.Version() > aCurrentNode.Version() + || aNewNode.Version() == aCurrentNode.Version() + && aNewNode.LeafDistance() <= aCurrentNode.LeafDistance() + && ( aNewNode.Type() == aCurrentNode.Type() + || aNewNode.Type() != MIAUpdateNode::EPackageTypeSA + && aCurrentNode.Type() == MIAUpdateNode::EPackageTypeSA + || aNewNode.Type() == MIAUpdateNode::EPackageTypePU + && aCurrentNode.Type() == MIAUpdateNode::EPackageTypeSP ) ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::ReplaceRecommendedL() end: ETrue"); + return ETrue; + } + else + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::ReplaceRecommendedL() end: EFalse"); + return EFalse; + } + } + + +TBool CIAUpdateNodeContainer::PackageTypeAcceptedL( + const CIAUpdateNode& aNode ) const + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::PackageTypeAcceptedL() begin"); + + if ( aNode.Type() == MIAUpdateNode::EPackageTypeServicePack + || aNode.Type() == MIAUpdateNode::EPackageTypeSA ) + { + // Service packs and SA type always accepted. + IAUPDATE_TRACE("[IAUPDATE] Upgrade packet type SA or node is service pack"); + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::PackageTypeAcceptedL() end: ETrue"); + return ETrue; + } + + TBool accepted( EFalse ); + + // The PU and SP packet types are accepted only if + // the content that will be updated already exists in the phone. + // Otherwise, the SA should be used. + RPointerArray< CIAUpdateNodeDependency > dependencies; + CleanupClosePushL( dependencies ); + + aNode.Details().GetDependenciesL( dependencies ); + + for ( TInt i = 0; i < dependencies.Count(); ++i ) + { + CIAUpdateNodeDependency* dep( dependencies[ i ] ); + if ( aNode.Uid() == dep->Uid() ) + { + // This dependency describes the upgrade dependency + // because the UIDs match. So, now we check if + // the upgrade dependency content is available in the phone. + TIAUpdateVersion version; + + // Check if the dependency file has already been installed + // and get the version of the application if it has been installed. + TBool installed = + IAUpdateUtils::IsAppInstalledL( dep->Uid(), version ); + + if ( installed + && version >= dep->VersionFloor() + && version <= dep->VersionRoof() ) + { + // The content was already installed and its version belonged + // to the required version range. So, upgrade content is accepted. + IAUPDATE_TRACE("[IAUPDATE] Accepted"); + accepted = ETrue; + break; + } + } + } + + // If dependencies to the previous versions of + // the same content were not found, PU and SP are not accepted. + CleanupStack::PopAndDestroy( &dependencies ); + + IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateNodeContainer::PackageTypeAcceptedL() end: %d", + accepted); + + return accepted; + } + + +TBool CIAUpdateNodeContainer::NodeAlreadyExists( const CIAUpdateNode& aNode ) const + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::NodeAlreadyExists() begin"); + + CIAUpdateNode* node( NULL ); + for ( TInt i = 0; i < iNodes.Count(); ++i ) + { + node = iNodes[ i ]; + if ( node->Equals( aNode ) ) + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::NodeAlreadyExists() end: ETrue"); + return ETrue; + } + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::NodeAlreadyExists() end: EFalse"); + + return EFalse; + } + + +void CIAUpdateNodeContainer::CreateSelfUpdateBundleL() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::CreateSelfUpdateBundleL() begin"); + + // Check if IAD exists and if self updater and NCD exist. + // Mark self updater and NCD hidden if necessary. + // Then, RemoveHiddenNodesFromHeadList will handle rest + // when it is called later in the head list creation process. + + CIAUpdateNode* iad( NULL ); + CIAUpdateNode* ncd( NULL ); + CIAUpdateNode* selfUpdater( NULL ); + + for ( TInt i = 0; i < iHeadNodes.Count(); ++i ) + { + CIAUpdateNode* node( iHeadNodes[ i ] ); + const TUid& nodeUid( node->Uid() ); + + if ( IAUpdateNodeFactory::IsIad( nodeUid ) ) + { + iad = node; + } + else if ( IAUpdateNodeFactory::IsNcd( nodeUid ) ) + { + ncd = node; + } + else if ( IAUpdateNodeFactory::IsUpdater( nodeUid ) ) + { + selfUpdater = node; + } + } + + // If IAD is given, then other self update related items should + // be set hidden. + if ( iad ) + { + IAUPDATE_TRACE("[IAUPDATE] IAD"); + // Because IAD is given, the possible NCD and self updater nodes + // should be set hidden and dependencies should be created for + // self update bundle. + // Notice, here we create the forced dependencies only from IAD. + if ( ncd ) + { + IAUPDATE_TRACE("[IAUPDATE] NCD"); + // Make NCD hidden and force IAD to depend on it. + ncd->ForceHidden( ETrue ); + // SetExcessDependencyL will also update the dependant info + // for the dependency node. + iad->SetExcessDependencyL( *ncd, ETrue ); + + // Because there is possibility that some service pack items + // depend on NCD but do not depend on IAD we need to force + // the dependency to IAD. This way if that service pack is + // chosen, the IAD that is visible in UI will also be chosen + // in UI and the flow can continue correctly. If IAD would not + // be forced, then NCD would not be installed because at the + // moment service packs do not support self update in IAD Engine + // side. If IAD does not exist, then NCD will be shown in UI + // and it would be selected if dependency requires that. + TInt ncdDependantCount( ncd->DependantNodes().Count() ); + const RPointerArray< CIAUpdateNode >& ncdDependants( + ncd->DependantNodes() ); + IAUPDATE_TRACE_1("[IAUPDATE] Ncd dependant count: %d", + ncdDependantCount); + for ( TInt i = 0; i < ncdDependantCount; ++i ) + { + CIAUpdateNode* ncdDependantNode( + ncdDependants[ i ] ); + if ( iad != ncdDependantNode ) + { + IAUPDATE_TRACE("[IAUPDATE] NCD dependant gets IAD as dependency"); + // The dependant was not iad itself. + // Force dependant to depend also from iad. + ncdDependantNode->SetExcessDependencyL( *iad, ETrue ); + } + } + } + if ( selfUpdater ) + { + IAUPDATE_TRACE("[IAUPDATE] Selfupdater"); + // Make selfupdater hidden and force IAD to depend on it. + selfUpdater->ForceHidden( ETrue ); + // SetExcessDependencyL will also update the dependant info + // for the dependency node. + iad->SetExcessDependencyL( *selfUpdater, ETrue ); + } + } + + // Above, dependencies were forced to make sure that self update + // bunlde is created and the self udpate flow will handle the items + // in correct order. Now, make sure that self updater depth and + // leaf distance are correct if both ncd and self updater exist. + // There is at least some sort of dependency also between them. + if ( selfUpdater && ncd ) + { + IAUPDATE_TRACE("[IAUPDATE] Self updater dependency for NCD"); + // Because NCD also requires self updater for its install, + // set the dependency depth and leaf distances also here. + // Then, the update flow will always try to install self + // updater before NCD. Notice, if server has not defined + // the dependency between selfupdater and NCD, then do not + // force the dependency here either. + // In case of IAD, the items will be hidden from UI. + // Then, forced dependency chains will not change functionality + // in UI. But, if IAD was missing and only NCD and selfupdater would + // exist, NCD and self updater should be shown in UI themselves. Then, + // UI side should not force dependency between those items, unless + // dependency is given in metadata. That is why we do not force + // excess dependency into the dependency chain here. Notice, that + // if IAD exists, then selfupdater is forced to its dependency above. + // So, self updater is part of the IAD bundle then. + ncd->SetExcessDependencyL( *selfUpdater, EFalse ); + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::CreateSelfUpdateBundleL() end"); + } + + +void CIAUpdateNodeContainer::RemoveHiddenNodesFromHeadList() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::RemoveHiddenNodesFromHeadList() begin"); + + TInt count( iHeadNodes.Count() ); + for ( TInt i = count - 1; i >= 0; --i ) + { + CIAUpdateNode* node( iHeadNodes[ i ] ); + if ( node->Hidden() ) + { + // Node is not deleted here because it is still part of the + // iNodes list that contains all the nodes. + IAUPDATE_TRACE("[IAUPDATE] Remove node"); + iHeadNodes.Remove( i ); + } + } + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::RemoveHiddenNodesFromHeadList() begin"); + } + + +void CIAUpdateNodeContainer::HandleServicePacksL() + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::HandleServicePacksL() begin"); + + // This array will contain the nodes that belong to + // the dependency hierarchy of the service pack. + RPointerArray< CIAUpdateNode > servicePackNodes; + CleanupClosePushL( servicePackNodes ); + + // Notice, here that we will force all the items under the found service pack + // as hidden. The main service pack will be shown in UI and possible service packs + // in dependencies will be hidden. + + for ( TInt i = 0; i < iNodes.Count(); ++i ) + { + CIAUpdateNode& node( *iNodes[ i ] ); + if ( node.Type() == MIAUpdateNode::EPackageTypeServicePack ) + { + IAUPDATE_TRACE("[IAUPDATE] Service pack node found"); + + if ( node.IsInstalled() ) + { + IAUPDATE_TRACE("[IAUPDATE] Force service pack hidden"); + // Because service pack is already installed, set + // it as hidden itself. Do not delete it because + // there may be some dependencies set to it before + // from other service packs. By setting node hidden, + // it will not be included into the main list. + node.ForceHidden( ETrue ); + } + + // Nodes of service pack are set to hidden. + // These nodes itself may have dependencies, but they are not forced hidden. + // + node.GetDependencyNodesL( servicePackNodes, ETrue ); + for ( TInt j = 0; j < servicePackNodes.Count(); ++j ) + { + CIAUpdateNode& servicePackNode( *servicePackNodes[ j ] ); + if ( !servicePackNode.IsSelfUpdate() ) + { + servicePackNode.ForceHidden( ETrue ); + } + } + // Reset the array, because we may use the array in + // next loops. + servicePackNodes.Reset(); + } + } + + CleanupStack::PopAndDestroy( &servicePackNodes ); + + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::HandleServicePacksL() end"); + } + + +TBool CIAUpdateNodeContainer::InstallCheckL( CIAUpdateNode& aNode ) const + { + IAUPDATE_TRACE("[IAUPDATE] CIAUpdateNodeContainer::InstallCheckL() begin"); + + TBool checkPassed( ETrue ); + + // Notice, that service packs are accepted even if they + // would be already installed. This is because service + // packs themselves contain dependencies and these dependencies + // may not be set yet. So, after all the nodes are available, + // the service packs can be checked separately. + if ( aNode.Type() != MIAUpdateNode::EPackageTypeServicePack ) + { + IAUPDATE_TRACE("[IAUPDATE] Not a service pack"); + + TIAUpdateVersion installedVersion; + TBool installed( + IAUpdateUtils::IsAppInstalledL( aNode.Uid(), installedVersion ) ); + // Notice that here we let the check pass also if node has the same version + // as the installed content. By accepting same version, the dependency chains + // will contain the currently installed node dependency information. Then, + // if newer dependencies are provided for the already installed content, + // they will be handled correctly for example inside service packs. + if ( installed ) + { + IAUPDATE_TRACE("[IAUPDATE] Content installed"); + if ( installedVersion > aNode.Version() ) + { + IAUPDATE_TRACE("[IAUPDATE] Newer version already installed."); + // If the installed version is newer, then think the node as + // installed. + checkPassed = EFalse; + } + else if ( installedVersion == aNode.Version() ) + { + IAUPDATE_TRACE("[IAUPDATE] Force node hidden"); + // Node is already installed but we still want to include + // it into the dependency chain. But, node should not be + // shown in the UI. So, force the installed node as hidden. + aNode.ForceHidden( ETrue ); + } + } + } + + IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateNodeContainer::InstallCheckL() end: %d", + checkPassed); + + return checkPassed; + }