diff -r 3f419852be07 -r 364021cecc90 smartinstaller/adm/src/ADMPackageInfo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smartinstaller/adm/src/ADMPackageInfo.cpp Wed Jun 30 11:01:26 2010 +0530 @@ -0,0 +1,809 @@ +/* +* Copyright (c) 2009-2010 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: +* CPackageInfo implementation +* +* +*/ + + +#include +#include +#include // SysUtil::DiskSpaceBelowCriticalLevel() + +#include "ADMXmlParser.h" +#include "ADMPackageInfo.h" +#include "ADMDownloadHandler.h" +#include "ADMAppUi.h" + +#include "macros.h" + +//TODO: Right place?! +// The extension of the changes file +_LIT(KChangesExt,"_changes.xml"); + +CPackageInfo* CPackageInfo::NewLC() + { + CPackageInfo* object = new ( ELeave ) CPackageInfo(); + CleanupStack::PushL( object ); + object->ConstructL(); + return object; + } + +CPackageInfo* CPackageInfo::NewL() + { + CPackageInfo* object = CPackageInfo::NewLC(); + CleanupStack::Pop(); + return object; + } + +void CPackageInfo::ConstructL() + { + } + +CPackageInfo::CPackageInfo() : + iPackageStatus(EPackageStatusUnknown), + iEdgeDlinkHdr(_FOFF(Edge, iEdgeDlink)), + iEdgeDlinkIter(iEdgeDlinkHdr) + { + } + +CPackageInfo::~CPackageInfo() + { + Edge* currentitem; + + iEdgeDlinkIter.SetToFirst(); + + // Clear the memory allocated for all Edges. + while((currentitem = iEdgeDlinkIter++) != NULL) + { + currentitem->iEdgeDlink.Deque(); + delete currentitem; + }; + + DELETE_IF_NONNULL( iPackageName ); + DELETE_IF_NONNULL( iVendor ); + DELETE_IF_NONNULL( iDateOfSubmission ); + DELETE_IF_NONNULL( iDateOfModification ); + DELETE_IF_NONNULL( iUrl ); + DELETE_IF_NONNULL( iDownloadUrl ); + DELETE_IF_NONNULL( iDepFileName ); + DELETE_IF_NONNULL( iChangesFileName ); + DELETE_IF_NONNULL( iSisPackageName ); + } + +void CPackageInfo::AddEdgeL(CPackageInfo* aDest) + { + User::LeaveIfNull(aDest); + + Edge* newEdge = Edge::NewL(this, aDest); + if (iEdgeDlinkHdr.IsEmpty()) + { + // This is the first Edge in the list + iEdgeDlinkHdr.AddFirst(*newEdge); + iEdgeDlinkIter.SetToFirst(); + } + else + { + // Append Edge to the list + iEdgeDlinkHdr.AddLast(*newEdge); + iEdgeDlinkIter.SetToLast(); + } + } + +void CPackageInfo::VisitL(CDepTree *aDepTree) + { + // TODO: Proper error handling + User::LeaveIfNull(aDepTree); + + Edge *e; + CPackageInfo *temp; + + if (iVisited == BEING_VISITED) + { + // Cycle is detected + aDepTree->iCyclePresent = ETrue; + return; + } + if (iVisited == DONE_VISITED) + { + // Node already visited added to the fetch list + return; + } + + iVisited = BEING_VISITED; + + // Visit all the Edges first + while(iEdgeDlinkIter!= NULL) + { + e = iEdgeDlinkIter--; + temp = e->GetVertexDst(); + temp->VisitL(aDepTree); + } + iVisited = DONE_VISITED; + + aDepTree->AddFetchPackageInfo(this); + } + +void CPackageInfo::SetDepFileNameL(const TDesC& aName) + { + __ASSERT_ALWAYS( aName.Length() > 0, User::Leave( KErrArgument) ); + + DELETE_IF_NONNULL( iDepFileName ); + iDepFileName = aName.AllocL(); + } + +//TODO : Clean possible? +void CPackageInfo::SetChangesFileNameL() + { + DELETE_IF_NONNULL( iChangesFileName ); + +#ifdef FEATURE_CHANGES_USE_UID + //By default, use Packageuid as the Changes filename. + //set the length as ( PackageUIDLength (8) + Changes_ext length ) + iChangesFileName = HBufC::NewL(8 + KChangesExt().Length()); + TPtr nameBufPtr = iChangesFileName->Des(); + nameBufPtr.Num(GetPackageUid(), EHex); + nameBufPtr.Append(KChangesExt); +#else + //Use Packagename as the Changes filename. + // If package name is NULL, then Package UID is used as Changes file name. + if( GetPackageName() != NULL ) + { + iChangesFileName = HBufC::NewL( GetPackageName()->Length() + + KChangesExt().Length() ); + TPtr nameBufPtr = iChangesFileName->Des(); + nameBufPtr.Copy(GetPackageName()->Des()); + + const TInt nameLength = GetPackageName()->LocateReverse('.'); + + if (nameLength != KErrNotFound) + { + //Remove the extension. + nameBufPtr.Delete( nameLength, GetPackageName()->Length() - nameLength); + } + nameBufPtr.Append(KChangesExt); + } + else + { + iChangesFileName = HBufC::NewL(8 + KChangesExt().Length()); + TPtr nameBufPtr = iChangesFileName->Des(); + nameBufPtr.Num(GetPackageUid(), EHex); + nameBufPtr.Append(KChangesExt); + } +#endif + } + +void CPackageInfo::SetSisFileNameL(const TDesC& aName) + { + __ASSERT_ALWAYS( aName.Length() > 0, User::Leave( KErrArgument) ); + + DELETE_IF_NONNULL( iSisPackageName ); + iSisPackageName = aName.AllocL(); + } + +// ----------------------------------------------------------------------------- +// Creates download URL for the package. The constructed URL is stored in +// member variable iDownloadUrl. The URL is constructed only once, the +// subsequent calls to this return iDownloadUrl. +// +// base_url/package/version/dep_file +// i.e. +// http://server.somewhere.net/root/qt/4.6.1/qt_dep.xml +// ----------------------------------------------------------------------------- +// +HBufC8* CPackageInfo::GetDownloadUrlL() + { +#ifdef FEATURE_CHANGES_DOWNLOAD_URL + if (iDownloadUrl) + { + return iDownloadUrl; + } + + // Url should never be null as specifying + // the Url in changes file is mandatory + User::LeaveIfNull(iUrl); + + TInt len = iUrl->Des().Length(); +#ifdef FEATURE_CHANGES_USE_UID + len += 8; // UID length as HEX string // iPackageName->Length(); +#else + len += iPackageName->Length(); +#endif + + // TVersion: iMajor(8bit), iMinor(8bit), iBuild(16bit) = 255 255 65535 + // delimiters: /qt/4.6.1/ => //../ = (len=5) + len += (3+3+5 + 5); + + HBufC8* url = HBufC8::NewL(len); + TPtr8 urlPtr = url->Des(); + // Construct the download path: + // base_url/package/version/dep_file + // i.e. + // http://server.somewhere.net/root/qt/4.6.1/qt_dep.xml + urlPtr.Copy(*iUrl); + + // Append forward slash if that's missing + if (urlPtr.Right(1)[0] != '/') + { + urlPtr.Append('/'); + } + +#ifdef FEATURE_CHANGES_USE_UID + urlPtr.AppendNum(iPackageUid, EHex); +#else + urlPtr.Append(iPackageName->Des()); +#endif + urlPtr.Append('/'); + urlPtr.AppendNum(iVersion.iMajor); + urlPtr.Append('.'); + urlPtr.AppendNum(iVersion.iMinor); + urlPtr.Append('.'); + urlPtr.AppendNum(iVersion.iBuild); + urlPtr.Append('/'); + + iDownloadUrl = url; + + return url; +#else + // Url should never be null as specifying + // the Url in changes file is mandatory + User::LeaveIfNull(iUrl); + return iUrl; +#endif // FEATURE_CHANGES_DOWNLOAD_URL + } + +#ifdef USE_LOGFILE +CDepTree* CDepTree::NewLC(RFileLogger& aLogger, const TDesC& aDownloadPath) + { + CDepTree* object = new ( ELeave ) CDepTree(aLogger); +#else +CDepTree* CDepTree::NewLC(const TDesC& aDownloadPath) + { + CDepTree* object = new ( ELeave ) CDepTree(); +#endif + CleanupStack::PushL( object ); + object->ConstructL(aDownloadPath); + return object; + } + +#ifdef USE_LOGFILE +CDepTree* CDepTree::NewL(RFileLogger& aLogger, const TDesC& aDownloadPath) + { + CDepTree* object = CDepTree::NewLC(aLogger, aDownloadPath); +#else +CDepTree* CDepTree::NewL(const TDesC& aDownloadPath) + { + CDepTree* object = CDepTree::NewLC(aDownloadPath); +#endif + CleanupStack::Pop(); + return object; + } + +#ifdef USE_LOGFILE +CDepTree::CDepTree(RFileLogger& aLogger) : + iLog(aLogger), +#else +CDepTree::CDepTree() : +#endif + iCurrentPackage(-1), + iSortedPackage(-1), + iCyclePresent(EFalse), + iPackageDlinkHdr(_FOFF(CPackageInfo,iPackageDlink)), + iPackageDlinkFetchIter(iPackageDlinkHdr) + { + iFetchList.Reset(); + iSortedList.Reset(); + } + +void CDepTree::ConstructL(const TDesC& aDownloadPath) + { + iDownloadPath = aDownloadPath.AllocL(); + } + +CDepTree::~CDepTree() + { + DELETE_IF_NONNULL( iDownloadPath ); + + CPackageInfo* currentPackage; + iPackageDlinkFetchIter.SetToFirst(); + + TDblQueIter PackageIter(iPackageDlinkHdr); + PackageIter.SetToFirst(); + + // Clear memory allocated for all packages + while((currentPackage = PackageIter++) != NULL) + { + currentPackage->iPackageDlink.Deque(); + delete currentPackage; + }; + + iFetchList.Close(); + iSortedList.Close(); + } + +const TLinearOrder CPackageInfo::KSortOrderByDrivePriority(CPackageInfo::CompareByDrivePriority); + +TInt CPackageInfo::CompareByDrivePriority( const CPackageInfo& aPackageOne, const CPackageInfo& aPackageTwo ) +{ + if ( (aPackageOne.GetDrivePriority()) > (aPackageTwo.GetDrivePriority()) ) + { + return -1; + } + else + { + return 1; + } +} + +TBool CDepTree::ConstructFetchListL() + { + if(iPackageDlinkHdr.IsEmpty()) + { + return EFalse; + } + + CPackageInfo *rootNode = iPackageDlinkHdr.First(); + rootNode->VisitL(this); + return ETrue; + } + +TBool CDepTree::SetDriveInfo() + { +#ifdef FEATURE_INSTALL_DRIVE_SELECTION + TInt driveIndex = 0; + CPackageInfo* currentNode = NULL; + TBool retStatus = ETrue; // Default to successful check + RArray driveLetters(EDriveZ-EDriveA+1); + RArray driveSpaces(EDriveZ-EDriveA+1); + RFs fs; + + if (fs.Connect() != KErrNone) + { + return EFalse; + } + + CleanupClosePushL(fs); + CleanupClosePushL(driveLetters); + CleanupClosePushL(driveSpaces); + + // Get the drive letters and spaces + TRAPD(err, GetDriveListL(fs, driveLetters, driveSpaces) ); + if (err != KErrNone) + { + return EFalse; + } + + // The packages are sorted based on priority + for (TInt index = 0; (index < iSortedList.Count()) && (retStatus != EFalse); index++ ) + { + //LOG2( "Index is %d", index ); + + currentNode = iSortedList[index]; + + const TChar mandatoryDriveLetter = currentNode->GetMandatoryInstallDrive(); + + if ( mandatoryDriveLetter ) + { + // Reset the driveIndex for drive selection of each package. + for ( driveIndex = 0; driveIndex < driveSpaces.Count(); driveIndex++ ) + { + // Check whether the current drive is the mandatory drive. + if ( driveLetters[driveIndex] == mandatoryDriveLetter ) + { + // Check whether there is enough space in mandatory drive. + if ( currentNode->GetInstallSize() < driveSpaces[driveIndex] ) + { + LOG3( "PkgUid 0x%08X installation forced to %c:", currentNode->GetPackageUid(), (char)driveLetters[driveIndex] ); + currentNode->SetInstallDrive(driveLetters[driveIndex]); + driveSpaces[driveIndex] -= currentNode->GetInstallSize(); + } + else + { + retStatus = EFalse; + } + break; + } + } + }// if ( mandatoryDriveLetter ) + else + { + // driveIndex is reset -> there could be a smaller package which fits in previous drive. + for ( driveIndex = 0; driveIndex < driveSpaces.Count(); driveIndex++ ) + { + if (currentNode->GetInstallSize() < driveSpaces[driveIndex]) + { + currentNode->SetInstallDrive(driveLetters[driveIndex]); + driveSpaces[driveIndex] -= currentNode->GetInstallSize(); + break; + } + } + }// else of if ( mandatoryDriveLetter ) + + if ( driveSpaces.Count() <= driveIndex ) + { + //Invalid mandatory drive specified. + retStatus = EFalse; + } + }// for loop of packages + + // All the dependent sis files are assigned the drives where they need to be installed. + // Now go thru the drives to figure out which one can accommodate the download. + + // This is done as a final act so that the memory is not blocked unnecessarily by download, + // which otherwise could be used to install a dependency sis. + + // Reset the driveIndex -> start from system drive. + for ( driveIndex = 0; ( driveIndex < driveSpaces.Count() ) && ( retStatus != EFalse ); driveIndex++ ) + { +/* + TInt drive; + RFs::CharToDrive(driveLetters[driveIndex], drive); + if (!SysUtil::DiskSpaceBelowCriticalLevelL(&fs, iMaxDownloadSize, drive)) + { +*/ + if ( driveSpaces[driveIndex] > iMaxDownloadSize ) + { + driveSpaces[driveIndex] -= iMaxDownloadSize; + iDownloadDrive = driveLetters[driveIndex]; + //Drive specified in download path is overriden. + iDownloadPath->Des()[0] = iDownloadDrive; + LOG4( "Download drive %c: free %ld (%.02f MB)", (char)iDownloadDrive, driveSpaces[driveIndex], (TReal)driveSpaces[driveIndex]/1048576.0 ); + break; + } + } + + // Fail, if no drive has space to max download size. + if ( driveSpaces.Count() <= driveIndex ) + { + // Fail: no memory. + retStatus = EFalse; + } + + CleanupStack::PopAndDestroy(3, &fs); + + return retStatus; + +#else + TInt64 driveSpace = iMaxDownloadSize; + CPackageInfo* currentNode = NULL; + const TInt installDrive = RFs::GetSystemDrive(); + TChar instDriveLetter; + TInt err; + TBool retStatus = ETrue; // Default to successful check + RFs fs; + + err = fs.Connect(); + if (err != KErrNone) + return EFalse; + + CleanupClosePushL(fs); + + RFs::DriveToChar(installDrive, instDriveLetter); + + // The install size of all dependent packages to be installed is added to the required driveSpace. + for (TInt index = 0; index < iSortedList.Count(); index++ ) + { + currentNode = iSortedList[index]; + driveSpace += currentNode->GetInstallSize(); + // Set the installation drive for the package + currentNode->SetInstallDrive(instDriveLetter); + } + + // check whether there is enough space in C to download and install all dependencies. + TRAP( err, retStatus = !SysUtil::DiskSpaceBelowCriticalLevelL( &fs, driveSpace, installDrive ) ); + + // If free space check fails, indicate not enough space + if (err != KErrNone) + retStatus = EFalse; + + CleanupStack::PopAndDestroy(&fs); + + return retStatus; +#endif + } + +void CDepTree::GetDriveListL(RFs& aRFs, RArray& aDriveLetters, RArray& aDriveSpaces) + { + // This is the LFSS free space threshold + const TInt freeSpaceAdjustment = 1024 * 384; + + // get information about drives + TDriveList driveList; + + // List all drives in the system + User::LeaveIfError( aRFs.DriveList(driveList) ); + + TVolumeInfo volInfo; + TInt64 volSpace = 0; + TChar systemDriveLetter; + const TInt systemDrive = RFs::GetSystemDrive(); + + // Not sure whether the first DriveLetter in the while loop corresponds to SystemDrive. + // Hence setting it explicitly as the first entry in Drive Array + RFs::DriveToChar(systemDrive, systemDriveLetter); + if (aRFs.Volume(volInfo, systemDrive) == KErrNone) + { + LOG4( "SysDrive %c: Free %ld (%.02f MB)", (char)systemDriveLetter, volInfo.iFree, (TReal)volInfo.iFree/1048576.0 ); + volSpace = volInfo.iFree - freeSpaceAdjustment; // bytes + if (volSpace < 0) + { + volSpace = 0; + } + } + + User::LeaveIfError(aDriveLetters.Append(systemDriveLetter)); + User::LeaveIfError(aDriveSpaces.Append(volSpace)); + + // Check all drives + for (TInt driveNumber = EDriveA; driveNumber <= EDriveZ; driveNumber++) + { + + if (!driveList[driveNumber]) + { + // Not a recognised drive + continue; + } + + if (aRFs.Volume(volInfo, driveNumber) != KErrNone) + { + // The volume is not usable (e.g. no media card inserted) + continue; + } + + if (driveNumber == systemDrive) + { + //System Drive, already added to the list + continue; + } + + if ( (volInfo.iDrive.iType==EMediaNotPresent) || + (volInfo.iDrive.iType==EMediaRom) || + (volInfo.iDrive.iType==EMediaRemote) || + (volInfo.iDrive.iType==EMediaRam) || + (volInfo.iDrive.iType==EMediaUnknown) ) + { + // Exclude drives not suitable for installation + continue; + } + + // Do not list read only and substituted drives as an option to install to + if (volInfo.iDrive.iDriveAtt & KDriveAttRom || + volInfo.iDrive.iDriveAtt & KDriveAttSubsted) + { + continue; + } + + const TInt64 volSpace = volInfo.iFree - freeSpaceAdjustment; // bytes + if (volSpace < 0) + { + // Volume space below 0 => skip drive + continue; + } + + TChar drvLetter; + User::LeaveIfError(RFs::DriveToChar(driveNumber, drvLetter)); + User::LeaveIfError(aDriveLetters.Append(drvLetter)); + User::LeaveIfError(aDriveSpaces.Append(volSpace)); + LOG4( "Drive %c: Free %ld (%.02f MB)", (char)drvLetter, volSpace, (TReal)volSpace/1048576.0 ); + } // for + } + +TBool CDepTree::IsDepTreeEmpty() const + { + return (iPackageDlinkHdr.IsEmpty()); + } + +TBool CDepTree::IsCyclePresent() const + { + return iCyclePresent; + } + +void CDepTree::AddPackageInfo(CPackageInfo *aPackageInfo) + { + if(iPackageDlinkHdr.IsEmpty()) + { + // This is the first node in the list + iPackageDlinkHdr.AddFirst(*aPackageInfo); + iPackageDlinkFetchIter.SetToFirst(); + } + else + { + // Append node to the list + iPackageDlinkHdr.AddLast(*aPackageInfo); + } + } + +TPtrC CDepTree::GetDownloadPath() const + { + return iDownloadPath->Des(); + } + +void CDepTree::AddFetchPackageInfo(CPackageInfo *aPackageInfo) + { + iFetchList.Append(aPackageInfo); + //Add the download/install sizes only if the package is not already installed. + if(aPackageInfo->iPackageStatus != EPackageInstalled) + { + iSortedList.InsertInOrder(aPackageInfo,CPackageInfo::KSortOrderByDrivePriority); + iTotalDownloadSize += aPackageInfo->iDownloadSize; + + if(aPackageInfo->iDownloadSize > iMaxDownloadSize) + { + iMaxDownloadSize = aPackageInfo->iDownloadSize; + } + } + } + +CPackageInfo* CDepTree::LocatePackageInDepTree(const TUint32& aDepPackageUid) + { + CPackageInfo* currentItem; + TDblQueIter PackageIter(iPackageDlinkHdr); + PackageIter.SetToFirst(); + + while((currentItem = PackageIter++) != NULL) + { + if(currentItem->iPackageUid == aDepPackageUid) + { + return currentItem; + } + }; + return NULL; + } + +CPackageInfo* CDepTree::GetNextNode() + { + CPackageInfo* nextNode; + + if(iPackageDlinkFetchIter++ != NULL) + { + // This is because the first node is the root package and + // tree is already created from it. + nextNode = iPackageDlinkFetchIter; + } + else + { + // This case should never arise, as the api + // should not get called. + nextNode = NULL; + } + return nextNode; + } + +CPackageInfo* CDepTree::GetFetchNode( TInt nodeLocation) + { + CPackageInfo* node = NULL; + + if (iFetchList.Count() > nodeLocation ) + { + node = iFetchList[nodeLocation]; + } + return node; + } + + +CPackageInfo* CDepTree::GetNextFetchNode() + { + CPackageInfo* nextNode = NULL; + + if (iFetchList.Count() > (iCurrentPackage+1) ) + { + nextNode = iFetchList[++iCurrentPackage]; + } + return nextNode; + } + +//TODO: Remove this. this is only for testing purpose. +//otherwise, there is never a need to get the sorted nodes separately. +CPackageInfo* CDepTree::GetNextSortedNode() + { + CPackageInfo* nextNode = NULL; + + if (iSortedList.Count() > (iSortedPackage+1) ) + { + nextNode = iSortedList[++iSortedPackage]; + } + return nextNode; + } + +CPackageInfo* CDepTree::GetPreviousFetchNode() + { + CPackageInfo* previousNode = NULL; + if (iCurrentPackage > 0) + { + previousNode = iFetchList[--iCurrentPackage]; + } + return previousNode; + } + +void CDepTree::RemoveDownloadedFiles(RFs& aRfs) + { + CPackageInfo* currentItem; + TDblQueIter PackageIter(iPackageDlinkHdr); + PackageIter.SetToFirst(); + + // Remove all the downloaded dep and changes file + while((currentItem = PackageIter) != NULL) + { + if ( currentItem->iDepFileName ) + { + DeleteFile(aRfs, *(currentItem->iDepFileName), *iDownloadPath); + } + if ( currentItem->iChangesFileName ) + { + DeleteFile(aRfs, *(currentItem->iChangesFileName), *iDownloadPath); + } + if ( currentItem->iSisPackageName ) + { + DeleteFile(aRfs, *(currentItem->iSisPackageName), *iDownloadPath); + } + PackageIter++; + } + } + +void CDepTree::DeleteFile(RFs& aRfs, const TDesC& aFileName, const TDesC& aFilePath ) + { + TFileName filename; + + filename.Copy(aFilePath); + filename.Append(aFileName); + aRfs.Delete(filename); + } + +CPackageInfo* CDepTree::GetRootNode() + { + if (iPackageDlinkHdr.IsEmpty()) + { + return NULL; + } + else + { + return iPackageDlinkHdr.First(); + } + } + +Edge* Edge::NewLC(CPackageInfo *aVtx1, CPackageInfo *aVtx2) + { + Edge* object = new ( ELeave ) Edge(aVtx1, aVtx2); + CleanupStack::PushL( object ); + object->ConstructL(); + return object; + } + +Edge* Edge::NewL(CPackageInfo *aVtx1, CPackageInfo *aVtx2) + { + Edge* object = Edge::NewLC(aVtx1, aVtx2); + CleanupStack::Pop(); + return object; + } + +void Edge::ConstructL() + { + } + +Edge::Edge(CPackageInfo *aVtx1, CPackageInfo *aVtx2) + { + iVertexOrg = aVtx1; + iVertexDst = aVtx2; + } + +CPackageInfo* Edge::GetVertexOrg() const + { + return iVertexOrg; + } + +CPackageInfo* Edge::GetVertexDst() const + { + return iVertexDst; + }