diff -r 000000000000 -r ba25891c3a9e ncdengine/provider/server/src/ncdnodedependencyimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ncdengine/provider/server/src/ncdnodedependencyimpl.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,771 @@ +/* +* Copyright (c) 2006-2008 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 CNcdNodeDependency class +* +*/ + + +#include "ncdnodedependencyimpl.h" + +#include "ncdnodemetadataimpl.h" +#include "ncddownloadinfo.h" +#include "catalogssession.h" +#include "catalogsbasemessage.h" +#include "ncdnodefunctionids.h" +#include "ncdnodeclassids.h" +#include "catalogsconstants.h" +#include "ncd_pp_dataentity.h" +#include "ncd_pp_dataentitycontent.h" +#include "ncd_pp_entitydependency.h" +#include "ncd_cp_query.h" +#include "ncdnodeidentifier.h" +#include "ncddependencyinfo.h" +#include "catalogsutils.h" +#include "ncderrors.h" +#include "ncdprotocolwords.h" +#include "ncdpurchasehistoryutils.h" + +#include "catalogsdebug.h" + +CNcdNodeDependency::CNcdNodeDependency( CNcdNodeMetaData& aParentMetaData, + NcdNodeClassIds::TNcdNodeClassId aClassId ) +: CNcdCommunicable(), + iParentMetaData( aParentMetaData ), + iClassId( aClassId ) + { + } + +void CNcdNodeDependency::ConstructL() + { + // These values have to be set. + } + + +CNcdNodeDependency* CNcdNodeDependency::NewL( CNcdNodeMetaData& aParentMetaData ) + { + CNcdNodeDependency* self = + CNcdNodeDependency::NewLC( aParentMetaData ); + CleanupStack::Pop( self ); + return self; + } + +CNcdNodeDependency* CNcdNodeDependency::NewLC( CNcdNodeMetaData& aParentMetaData ) + { + CNcdNodeDependency* self = + new( ELeave ) CNcdNodeDependency( + aParentMetaData, + NcdNodeClassIds::ENcdNodeDependencyClassId ); + CleanupClosePushL( *self ); + self->ConstructL(); + return self; + } + + +CNcdNodeDependency::~CNcdNodeDependency() + { + DLTRACEIN(("")); + + // Delete member variables here + iDependencyNodeTargets.ResetAndDestroy(); + iDependencyContentTargets.ResetAndDestroy(); + + iOldDependencyContentTargets.ResetAndDestroy(); + // Do not delete parent metadata because it is not owned here. + + DLTRACEOUT(("")); + } + + +NcdNodeClassIds::TNcdNodeClassId CNcdNodeDependency::ClassId() const + { + return iClassId; + } + + +const RPointerArray& CNcdNodeDependency::ContentTargets() const + { + return iDependencyContentTargets; + } + + +const RPointerArray& CNcdNodeDependency::NodeTargets() const + { + return iDependencyNodeTargets; + } + + +// Internalization from the protocol + +void CNcdNodeDependency::InternalizeL( MNcdPreminetProtocolDataEntity& aData ) + { + DLTRACEIN(("")); + + // First create the new values + + // Notice that ownerships are not transferred from the parser objects. + const MNcdPreminetProtocolDataEntityContent* downloadableContent = + aData.DownloadableContent(); + if ( downloadableContent == NULL ) + { + DLINFO(("No data present")); + // Content data not present. + User::Leave( KErrNotFound ); + } + + // Reset the array here. + // If something here fails after this. Then, the owner of this class object + // should remove this class object from the metadata anyway. + iDependencyNodeTargets.ResetAndDestroy(); + iDependencyContentTargets.ResetAndDestroy(); + + const MNcdPreminetProtocolEntityDependency* dependency( NULL ); + CNcdNodeIdentifier* identifier( NULL ); + CNcdDependencyInfo* dependencyInfo(NULL ); + + DLTRACE(("Going through %d entity dependencies", + downloadableContent->EntityDependencyCount() )); + + for ( TInt i = 0; i < downloadableContent->EntityDependencyCount(); ++i ) + { + // Check what nodes depend on this node. + dependency = &downloadableContent->EntityDependencyL( i ); + if ( dependency->Type() == EDependency ) + { + MNcdPurchaseDownloadInfo::TContentUsage usage = + DetermineContentUsage( dependency->Name() ); + + // Check if the dependency should be loaded as a node when + // the download responsibility is transferred to the proxy side + // or should we just download the content directly in server side. + if ( dependency->EntityId() != KNullDesC && + // If the name of the dependency contains any of the launcher keywords, + // we handle it like a SIS-dependency even if it is really a entity + // dependency. + usage == MNcdPurchaseDownloadInfo::EDependency ) + { + DLINFO((_L("Dependency entity id: %S"), &dependency->EntityId())); + // The dependency information contains the + // entity id of the target node. + identifier = + CNcdNodeIdentifier::NewL( iParentMetaData.Identifier().NodeNameSpace(), + dependency->EntityId(), + iParentMetaData.Identifier().ServerUri(), + iParentMetaData.Identifier().ClientUid() ); + // Note, that identifier ownership is transferred here. + dependencyInfo = + CNcdDependencyInfo::NewLC( dependency->Name(), + dependency->ContentVersion(), + dependency->ContentId(), + identifier ); + iDependencyNodeTargets.AppendL( dependencyInfo ); + CleanupStack::Pop( dependencyInfo ); + } + else + { + // Ownership is not transferred here. + const MNcdPreminetProtocolDownload* downloadDetails( + dependency->DownloadDetails() ); + if ( downloadDetails != NULL || + // Launchers don't need to have download details + usage != MNcdPurchaseDownloadInfo::EDependency ) + { + DLINFO(("Download the content directly. New node will not be created.")); + // Copy the content object and insert it into the content array. + // The purchase operation may use the array content when checking + // if dependencies should be downloaded and installed. + CNcdDownloadInfo* content( + CNcdDownloadInfo::NewLC( *dependency ) ); + + TBool isLauncher = SetContentUsage( *dependency, *content ); + // Dependencies are not launchable but launchers are + content->SetLaunchable( isLauncher ); + + iDependencyContentTargets.AppendL( content ); + + // Array took ownership. So, do not delete. + CleanupStack::Pop( content ); + DLTRACE(("Download info added")); + } + } + } + } + + + if ( iDependencyContentTargets.Count() == 0 ) + { + // No dependency content targets so the current item on the server + // doesn't have any dependencies so we don't show any + // through the API + iOldDependencyContentTargets.ResetAndDestroy(); + } + + if ( iDependencyNodeTargets.Count() == 0 + && iDependencyContentTargets.Count() == 0 ) + { + // No Dependency data was found. + // So, inform about that to the owner of this class object. + DLINFO(("No content")); + User::Leave( KErrNotFound ); + } + + DLTRACEOUT(("")); + } + + +TBool CNcdNodeDependency::InternalizeFromPurchaseDetailsL( + const MNcdPurchaseDetails& aDetails ) + { + DLTRACEIN(("")); + + iOldDependencyContentTargets.ResetAndDestroy(); + + TArray dlInfo ( aDetails.DownloadInfoL() ); + + if ( !dlInfo.Count() ) + { + DLTRACEOUT(("No download infos so no dependencies")); + return EFalse; + } + + TArray installInfo ( aDetails.InstallInfoL() ); + + + for ( TInt i = 0; i < dlInfo.Count(); ++i ) + { + // Launchers are dependencies + if ( NcdPurchaseHistoryUtils::IsDependency( *dlInfo[i] ) ) + { + DLTRACE(("Internalizing dependency from purchase history")); + DASSERT( installInfo.Count() > i ); + CNcdDownloadInfo* info = CNcdDownloadInfo::NewLC( + *dlInfo[i], + installInfo[i]->ApplicationUid(), + installInfo[i]->ApplicationVersion() ); + iOldDependencyContentTargets.AppendL( info ); + CleanupStack::Pop( info ); + } + else + { + // Dependencies are before other content so we + // can stop here + break; + } + } + + if ( iOldDependencyContentTargets.Count() ) + { + DLTRACEOUT(("Internalization successful")); + return ETrue; + } + + DLTRACEOUT(("Nothing to internalize")); + return EFalse; + } + + +TBool CNcdNodeDependency::UpdateDependencyStatesL() + { + DLTRACEIN(("")); + MoveOldContentTargetsToNew(); + + TBool allInstalled = ETrue; + + // Check if the dependencies have already been installed + for ( TInt i = 0; i < iDependencyContentTargets.Count(); ++i ) + { + // allInstalled becomes false if any of the dependencies is + // not installed + allInstalled = allInstalled && + UpdateDependencyStateL( *iDependencyContentTargets[i] ); + } + + + // Check if the dependencies have already been installed + for ( TInt i = 0; i < iDependencyNodeTargets.Count(); ++i ) + { + // EApplicationOlderVersionInstalled & EApplicationNotInstalled + // are considered as missing + if ( CNcdProviderUtils::IsApplicationInstalledL( + iDependencyNodeTargets[i]->Uid(), + iDependencyNodeTargets[i]->Version() ) <= + ENcdApplicationNotInstalled ) + { + DLTRACE(("Dependency not installed, updating state")); + iDependencyNodeTargets[i]->SetDependencyState( + ENcdDependencyMissing ); + allInstalled = EFalse; + } + else + { + iDependencyNodeTargets[i]->SetDependencyState( + ENcdDependencyInstalled ); + + } + } + + DLTRACEOUT(("allInstalled: %d", allInstalled)); + return allInstalled; + } + + +void CNcdNodeDependency::UpdateDependenciesL( + CNcdPurchaseDetails& aDetails ) const + { + DLTRACEIN(("")); + NcdPurchaseHistoryUtils::UpdateDependenciesL( + aDetails, + iDependencyContentTargets ); + } + + +// Internalization from and externalization to the database + +void CNcdNodeDependency::ExternalizeL( RWriteStream& aStream ) + { + DLTRACEIN(("")); + + if ( iDependencyNodeTargets.Count() == 0 + && iDependencyContentTargets.Count() == 0 ) + { + DLERROR(("No content")); + DASSERT( EFalse ); + User::Leave( KErrNotFound ); + } + + // First insert data that the creator of this class object will use to + // create this class object. Class id informs what class object + // will be created. + + aStream.WriteInt32L( iClassId ); + + ExternalizeNodeDependencyArrayL( aStream ); + ExternalizeContentDependencyArrayL( aStream ); + + DLTRACEOUT(("")); + } + +void CNcdNodeDependency::InternalizeL( RReadStream& aStream ) + { + DLTRACEIN(("")); + + // Read the class id first because it is set to the stream in internalize + // function and it is not read from the stream anywhere else. + TInt classId( aStream.ReadInt32L() ); + if ( classId != ClassId() ) + { + DLTRACE(("Wrong class id")); + DASSERT( EFalse ); + // Leave because the stream does not match this class object + User::Leave( KErrCorrupt ); + } + + InternalizeNodeDependencyArrayL( aStream ); + InternalizeContentDependencyArrayL( aStream ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeDependency::ReceiveMessage( MCatalogsBaseMessage* aMessage, + TInt aFunctionNumber ) + { + DLTRACEIN(("")); + + DASSERT( aMessage ); + + // Now, we can be sure that rest of the time iMessage exists. + // This member variable is set for the CounterPartLost function. + iMessage = aMessage; + + TInt trapError( KErrNone ); + + // Check which function is called by the proxy side object. + // Function number are located in ncdnodefunctinoids.h file. + switch( aFunctionNumber ) + { + case NcdNodeFunctionIds::ENcdInternalize: + // Internalize the proxy side according to the data + // of this object. + TRAP( trapError, InternalizeRequestL( *aMessage ) ); + break; + + case NcdNodeFunctionIds::ENcdRelease: + // The proxy does not want to use this object anymore. + // So, release the handle from the session. + ReleaseRequest( *aMessage ); + break; + + default: + DLERROR(("Unidentified function request")); + DASSERT( EFalse ); + break; + } + + if ( trapError != KErrNone ) + { + // Because something went wrong, the complete has not been + // yet called for the message. + // So, inform the client about the error if the + // message is still available. + aMessage->CompleteAndRelease( trapError ); + } + + // Because the message should not be used after this, set it NULL. + // So, CounterPartLost function will know that no messages are + // waiting the response at the moment. + iMessage = NULL; + + DLTRACEOUT(("")); + } + +void CNcdNodeDependency::CounterPartLost( const MCatalogsSession& aSession ) + { + // This function may be called whenever -- when the message is waiting + // response or when the message does not exist. + // iMessage may be NULL here, because in the end of the + // ReceiveMessage it is set to NULL. The life time of the message + // ends shortly after CompleteAndRelease is called. + if ( iMessage != NULL ) + { + iMessage->CounterPartLost( aSession ); + } + } + + +void CNcdNodeDependency::InternalizeRequestL( MCatalogsBaseMessage& aMessage ) + { + DLTRACEIN(("")); + + CBufBase* buf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( buf ); + + RBufWriteStream stream( *buf ); + CleanupClosePushL( stream ); + + + // Include all the necessary node data to the stream + ExternalizeDataForRequestL( stream ); + + + // Commits data to the stream when closing. + CleanupStack::PopAndDestroy( &stream ); + + + // If this leaves, ReceiveMessage will complete the message. + // NOTE: that here we expect that the buffer contains at least + // some data. So, make sure that ExternalizeDataForRequestL inserts + // something to the buffer. + aMessage.CompleteAndReleaseL( buf->Ptr( 0 ), KErrNone ); + + + DLTRACE(("Deleting the buf")); + CleanupStack::PopAndDestroy( buf ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeDependency::ExternalizeDataForRequestL( RWriteStream& aStream ) + { + DLTRACEIN(("")); + + if ( IsObsolete() ) + { + DLINFO(("Set as obsolete. This means that server has removed the object.")); + User::Leave( KNcdErrorObsolete ); + } + + UpdateDependencyStatesL(); + + // First insert data that the creator of this class object will use to + // create this class object. Class id informs what class object + // will be created. + + aStream.WriteInt32L( iClassId ); + + ExternalizeNodeDependencyArrayL( aStream ); + + // Also externalize some information about the possible content dependencies for the + // proxy + ExternalizeContentDependencyArrayForRequestL( aStream ); + + DLTRACEOUT(("")); + } + +void CNcdNodeDependency::ReleaseRequest( MCatalogsBaseMessage& aMessage ) const + { + DLTRACEIN(("")); + + // Decrease the reference count for this object. + // When the reference count reaches zero, this object will be destroyed + // and removed from the session. + MCatalogsSession& requestSession( aMessage.Session() ); + TInt handle( aMessage.Handle() ); + + // Send complete information back to proxy. + aMessage.CompleteAndRelease( KErrNone ); + + // Remove this object from the session. + requestSession.RemoveObject( handle ); + + DLTRACEOUT(("")); + } + + +void CNcdNodeDependency::ExternalizeNodeDependencyArrayL( RWriteStream& aStream ) + { + DLTRACEIN(("")); + + aStream.WriteInt32L( iDependencyNodeTargets.Count() ); + for ( TInt i = 0; i < iDependencyNodeTargets.Count(); ++i ) + { + iDependencyNodeTargets[ i ]->ExternalizeL( aStream ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeDependency::InternalizeNodeDependencyArrayL( RReadStream& aStream ) + { + DLTRACEIN(("")); + + CNcdDependencyInfo* dependencyInfo( NULL ); + TInt count( aStream.ReadInt32L() ); + + iDependencyNodeTargets.ResetAndDestroy(); + for( TInt i = 0; i < count; ++i ) + { + dependencyInfo = CNcdDependencyInfo::NewLC( aStream ); + iDependencyNodeTargets.AppendL( dependencyInfo ); + CleanupStack::Pop( dependencyInfo ); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeDependency::ExternalizeContentDependencyArrayForRequestL( RWriteStream& aStream ) + { + DLTRACEIN(("")); + + aStream.WriteInt32L( iDependencyContentTargets.Count() ); + CNcdDownloadInfo* downloadInfo( NULL ); + CNcdDependencyInfo* dependencyInfo( NULL ); + // Notice that here we will give dependency info objects to the proxy side. + for ( TInt i = 0; i < iDependencyContentTargets.Count(); ++i ) + { + downloadInfo = iDependencyContentTargets[ i ]; + dependencyInfo = CNcdDependencyInfo::NewLC( downloadInfo->ContentName(), + downloadInfo->ContentVersion(), + downloadInfo->ContentId(), + NULL ); + dependencyInfo->SetDependencyState( downloadInfo->DependencyState() ); + dependencyInfo->ExternalizeL( aStream ); + CleanupStack::PopAndDestroy( dependencyInfo ); + dependencyInfo = NULL; + } + + DLTRACEOUT(("")); + } + + +void CNcdNodeDependency::ExternalizeContentDependencyArrayL( RWriteStream& aStream ) + { + DLTRACEIN(("")); + + aStream.WriteInt32L( iDependencyContentTargets.Count() ); + for ( TInt i = 0; i < iDependencyContentTargets.Count(); ++i ) + { + iDependencyContentTargets[ i ]->ExternalizeL( aStream ); + } + + DLTRACEOUT(("")); + } + +void CNcdNodeDependency::InternalizeContentDependencyArrayL( RReadStream& aStream ) + { + DLTRACEIN(("")); + + CNcdDownloadInfo* content( NULL ); + TInt count( aStream.ReadInt32L() ); + + iDependencyContentTargets.ResetAndDestroy(); + for( TInt i = 0; i < count; ++i ) + { + content = CNcdDownloadInfo::NewLC(); + content->InternalizeL( aStream ); + iDependencyContentTargets.AppendL( content ); + CleanupStack::Pop( content ); + } + + DLTRACEOUT(("")); + } + + +// This moves dependencies read from the purchase history as the +// current dependencies if there are no current dependencies. +// This is used to ensure that MNcdNodeDependency show dependencies +// correctly even if the node cache has been cleared but the +// item has been bought +void CNcdNodeDependency::MoveOldContentTargetsToNew() + { + DLTRACEIN(("")); + + if ( iDependencyContentTargets.Count() == 0 ) + { + DLTRACE(("Moving dependencies")); + iDependencyContentTargets.Close(); + iDependencyContentTargets = iOldDependencyContentTargets; + iOldDependencyContentTargets = RPointerArray(); + } + } + + +TBool CNcdNodeDependency::UpdateDependencyStateL( + CNcdDownloadInfo& aContentTarget ) + { + DLTRACEIN(("")); + // Compare current dependencies to those read from purchase history + // If old dependencies exist: + // - they determine whether dependency is installed or not + // - their version is compared to the one received in the protocol + // if protocol contains newer, then that is used + TInt oldCount = iOldDependencyContentTargets.Count(); + for ( TInt i = 0; i < oldCount; ++i ) + { + if ( aContentTarget.ContentId() + == iOldDependencyContentTargets[i]->ContentId() ) + { + TInt comp = 0; + TRAPD( err, comp = CNcdProviderUtils::CompareVersionsL( + iOldDependencyContentTargets[i]->ContentVersion(), + aContentTarget.ContentVersion() ) ); + LeaveIfNotErrorL( err, KErrArgument, KErrGeneral ); + + TNcdApplicationStatus status = + CNcdProviderUtils::IsApplicationInstalledL( + iOldDependencyContentTargets[i]->ContentId(), + iOldDependencyContentTargets[i]->ContentVersion() ); + + + TBool installed = SetDependencyState( + aContentTarget, status, comp, EFalse ); + + DLTRACEOUT(("Installed: %d", installed)); + return installed; + } + } + + + // This is executed only if the dependency is not found from the + // old dependencies which means that either there were no old dependencies + // old dependencies have been updated on the server + TNcdApplicationStatus status = + CNcdProviderUtils::IsApplicationInstalledL( + aContentTarget.ContentId(), + aContentTarget.ContentVersion() ); + + // Give 0 as version comp status so that state is set either + // installed or missing, but if oldCount != 0 then the state will be + // either installed or upgrade available + TBool installed = SetDependencyState( + aContentTarget, status, 0, oldCount ); + + DLTRACEOUT(("Installed: %d", installed)); + return installed; + } + + + +TBool CNcdNodeDependency::SetDependencyState( + CNcdDownloadInfo& aContentTarget, + TNcdApplicationStatus aStatus, + TInt aVersionCompResult, + TBool aIsUpgrade ) + { + DLTRACEIN(("")); + // Dependency is installed if the correct or newer version is installed + TBool installed = + ( aStatus == ENcdApplicationInstalled ) || + ( aStatus == ENcdApplicationNewerVersionInstalled ); + + // Update dependency state according to installation status and + // version numbers + if ( installed ) + { + if ( aVersionCompResult >= 0 ) + { + DLTRACE(("Same or newer installed")); + aContentTarget.SetDependencyState( + ENcdDependencyInstalled ); + } + else + { + DLTRACE(("Older version installed")); + aContentTarget.SetDependencyState( + ENcdDependencyUpgradeAvailable ); + } + } + // if old dependencies are installed, new uninstalled are considered to + // be upgrades + else if ( aIsUpgrade ) + { + DLTRACE(("Dependency not installed but it's considered as an upgrade")); + aContentTarget.SetDependencyState( + ENcdDependencyUpgradeAvailable ); + } + else + { + DLTRACE(("Dependency not installed")); + aContentTarget.SetDependencyState( + ENcdDependencyMissing ); + + } + return installed; + } + + +TBool CNcdNodeDependency::SetContentUsage( + const MNcdPreminetProtocolEntityDependency& aDependency, + CNcdDownloadInfo& aTarget ) const + { + DLTRACEIN(("")); + MNcdPurchaseDownloadInfo::TContentUsage usage = + DetermineContentUsage( aDependency.Name() ); + + aTarget.SetContentUsage( usage ); + + // Must return ETrue if the dependency is a launcher application + return usage != MNcdPurchaseDownloadInfo::EDependency; + } + + +MNcdPurchaseDownloadInfo::TContentUsage CNcdNodeDependency::DetermineContentUsage( + const TDesC& aDependencyName ) const + { + MNcdPurchaseDownloadInfo::TContentUsage usage = + MNcdPurchaseDownloadInfo::EDependency; + + if ( aDependencyName.MatchF( KNcdLauncher ) != KErrNotFound ) + { + DLTRACE(("Launcher")); + usage = MNcdPurchaseDownloadInfo::ELauncher; + } + else if ( aDependencyName.MatchF( KNcdLauncherOpen ) != KErrNotFound ) + { + DLTRACE(("Launcher/open")); + usage = MNcdPurchaseDownloadInfo::ELauncherOpen; + } + return usage; + }