/*
* 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 )
{
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;
}