diff -r 3f419852be07 -r 364021cecc90 smartinstaller/adm/src/ADMStateMachine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smartinstaller/adm/src/ADMStateMachine.cpp Wed Jun 30 11:01:26 2010 +0530 @@ -0,0 +1,2340 @@ +/* +* 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: +* State machine implementation +* +* +*/ + + +#include +#include "ADMApplication.h" // KUidADMApp +#include "ADMStateMachine.h" +#include "ADMStateMachineDebug.h" +#include "globals.h" +#include "config.h" + +#include +#include // SysUtil::DiskSpaceBelowCriticalLevel() +#include +#include + +// Use Char 0xFF to indicate no drive is selected +const TChar KNoDriveSelected = 0xFF; + +// Used to force the progress bar to finish +const TInt KProgressBarFullValue = 100; + +TInt CompareVersions(TVersion& version1, TVersion& version2); + +// --------------------------------------------------------------------------- +// Panic +// +// Raises a panic with specific panic code. +// --------------------------------------------------------------------------- +// +LOCAL_C inline void Panic(TInt aPanicCode) + { + _LIT(KPanic, "SmartInst-SM"); + User::Panic( KPanic, aPanicCode ); + } + +LOCAL_C inline void Panic(TInt aPanicCode, TInt aState) + { + _LIT(KPanic, "SmartInst-SM"); + const TInt reason = aState*100 + aPanicCode; + User::Panic( KPanic, reason ); + } + +// ----------------------------------------------------------------------------- +// Default constructor +// ----------------------------------------------------------------------------- +// +CStateMachine::CStateMachine(CADMAppUi& aAppUi) : + CActive(EPriorityNormal), + iAppUi(aAppUi), + iFailedState( CStateFactory::EStateLast ) + { + CActiveScheduler::Add(this); + } + +// ----------------------------------------------------------------------------- +// NewL() +// ----------------------------------------------------------------------------- +// +CStateMachine* CStateMachine::NewL(CADMAppUi& aAppUi, const TDesC& aDownloadPath) + { + CStateMachine* self = new (ELeave) CStateMachine(aAppUi); + CleanupStack::PushL(self); + self->ConstructL(aDownloadPath); + CleanupStack::Pop(); + return self; + } + +// ----------------------------------------------------------------------------- +// ConstructL() +// ----------------------------------------------------------------------------- +// +void CStateMachine::ConstructL(const TDesC& aDownloadPath) + { + // Create a local semaphore for handling the download deleting + // This must be the first to be created and last to be destroyed. + iDlDeletingSemaphore.CreateLocal(0); + + iXmlParser = CXmlParser::NewL(); + iSisParser = CSisParser::NewL(); + +#ifdef USE_LOGFILE + iInstallManager = CInstallManager::NewL(this, iAppUi.iLog); + iDepTree = CDepTree::NewL(iAppUi.iLog, aDownloadPath); + iDownloadHandler = CDownloadHandler::NewL(this, iAppUi.iLog, !(iAppUi.EikonEnv()->StartedAsServerApp())); +#else + iInstallManager = CInstallManager::NewL(this); + iDepTree = CDepTree::NewL(aDownloadPath); + iDownloadHandler = CDownloadHandler::NewL(this, !(iAppUi.EikonEnv()->StartedAsServerApp())); +#endif + + iStateFactory = CStateFactory::NewL(*this); + } + +// ----------------------------------------------------------------------------- +// Destructor +// ----------------------------------------------------------------------------- +// +CStateMachine::~CStateMachine() + { + if (IsActive()) + { + Cancel(); + } + delete iStateFactory; + delete iDownloadHandler; + delete iInstallManager; + delete iDepTree; + delete iSisParser; + delete iXmlParser; + delete iDownloadedFile; + + iDlDeletingSemaphore.Close(); + } + +// ----------------------------------------------------------------------------- +// Updates the displayed progress bar +// ----------------------------------------------------------------------------- +// +void CStateMachine::UpdateProgressBar(TInt aStartValue, TInt aEndValue) + { + iAppUi.iProgress->Update(aStartValue, aEndValue); + } + +// ----------------------------------------------------------------------------- +// Removes the displayed progress bar +// ----------------------------------------------------------------------------- +// +void CStateMachine::StopProgressBar() const + { + iAppUi.iProgress->Cancel(); + } + +// ----------------------------------------------------------------------------- +// Sets the failure state and reason for exit +// ----------------------------------------------------------------------------- +// +void CStateMachine::SetFailureReason(TExitReason aReason) + { + iAppUi.iExitReason = aReason; + iFailedState = (CStateFactory::TAppState)StateIndex(); + // If the device is not supported, don't create resume info + if (aReason == EDeviceNotSupported) + { + iAppUi.iIsResumeRequired = EFalse; + } + } + +// ----------------------------------------------------------------------------- +// Shows the download query dialog with a specified text. +// @param aResourceId Resource ID for the text to be shown. +// @param aInfoResourceId Resource ID for additional information text. +// ----------------------------------------------------------------------------- +// +void CStateMachine::ShowDownloadQueryL(const TInt aResourceId, const TInt aInfoResourceId) + { + iAppUi.ShowDownloadQueryL( aResourceId, aInfoResourceId ); + } + +// ----------------------------------------------------------------------------- +// Shows wait note +// ----------------------------------------------------------------------------- +// +void CStateMachine::ShowWaitNoteL(const TInt aResourceId) + { + iAppUi.ShowWaitNoteL( aResourceId, ETrue ); + } + +// ----------------------------------------------------------------------------- +// Shows wait note with the specified values +// ----------------------------------------------------------------------------- +// +void CStateMachine::ShowInstallingWaitNoteL(const TInt aCurrent, const TInt aLast) + { + iAppUi.ShowWaitNoteNumL( R_ADM_INSTALLING_TEXT, aCurrent, aLast ); + } + +// ----------------------------------------------------------------------------- +// Shows the progress bar with the specified values +// ----------------------------------------------------------------------------- +// +void CStateMachine::ShowProgressBarL(const TInt aStartValue, const TInt aEndValue, const TInt aCurrent, const TInt aLast) + { + iAppUi.ShowGlobalProgressL( R_ADM_DOWNLOADING_TEXT, + aStartValue, aEndValue, + aCurrent, aLast ); + } + +// ----------------------------------------------------------------------------- +// Stops the wait note +// ----------------------------------------------------------------------------- +// +void CStateMachine::StopWaitNote() + { + iAppUi.CancelWaitNote(); + } + +// ----------------------------------------------------------------------------- +// Starts the state machine from the initial state. +// ----------------------------------------------------------------------------- +// +void CStateMachine::Start() + { + LOG( "+ CStateMachine::Start()" ); + + // If the initial state has been set, don't change it here + if (!iState) + { + iState = &iStateFactory->GetState( CStateFactory::EStateSetup ); + } + + iStatus = KRequestPending; + SetActive(); + // send signal that this request has completed + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + +// ----------------------------------------------------------------------------- +// Stops the state machine +// ----------------------------------------------------------------------------- +// +void CStateMachine::Stop() + { + LOG( "+ CStateMachine::Stop()" ); + Cancel(); + iDepTree->RemoveDownloadedFiles( FsSession() ); + // Log statistics. Prevent divide-by-zero. + if (iDownloadTime == 0) + iDownloadTime = 1; + LOG4( "Total %d kB in %.1f s, avg %.2f kB/s", + iCumulativeDownloaded/1024, + iDownloadTime/1000000.0, + iCumulativeDownloaded*1000000.0 / iDownloadTime / 1024.0); + } + +// ----------------------------------------------------------------------------- +// Returns current state ID +// ----------------------------------------------------------------------------- +// +TInt CStateMachine::StateIndex() const + { + return iStateFactory->StateIndex(iState); + } + +// ----------------------------------------------------------------------------- +// Request the next state we want the application to go to +// ----------------------------------------------------------------------------- +// +void CStateMachine::SetState(const CStateFactory::TAppState aState) + { + SetState( iStateFactory->GetState(aState) ); + } + +// ----------------------------------------------------------------------------- +// Set the starting state of the state machine, but do not invoke state change +// ----------------------------------------------------------------------------- +// +void CStateMachine::SetStartState(const CStateFactory::TAppState aState) + { + iState = &iStateFactory->GetState( aState ); + } + +// ----------------------------------------------------------------------------- +// Request the next state we want the application to go to +// ----------------------------------------------------------------------------- +// +void CStateMachine::SetState(TState& aState) + { + LOG3( "+ SetState(): %d -> %d", iStateFactory->StateIndex(iState), iStateFactory->StateIndex(&aState) ); + + iState->Exit(); + iState = &aState; + SetActive(); + // send signal that this request has completed + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + + //LOG2( "- SetState(): now in %d", iStateFactory->StateIndex(iState) ); + } + +// ----------------------------------------------------------------------------- +// Active object heart +// ----------------------------------------------------------------------------- +// +void CStateMachine::RunL() + { + //LOG2( "+ CStateEngine::RunL(): state=%d", iStateFactory->StateIndex(iState) ); + iState->Enter(); + //LOG2( "- CStateEngine::RunL(): %d", iStateFactory->StateIndex(iState) ); + } + +// ----------------------------------------------------------------------------- +// Handles the leaves from RunL(). +// +// SPECIFIC CASES NOT DONE YET: +// - +// ----------------------------------------------------------------------------- +// +TInt CStateMachine::RunError(const TInt aError) + { + LOG3("+ SM::RunError(): %d, %d", StateIndex(), aError ); + + TExitReason err = EInstallationFailed; + + switch ( aError ) + { + case KLeaveExit: + { + // TODO: Ugly hack: we need to return KLeaveExit as RunError() + // gets called when application exit is called within RunL(). + //LOG( "Exit() left" ); + return KLeaveExit; + } + case SwiUI::KSWInstErrUserCancel: + { + LOG( "User cancelled installation" ); + // User cancelled the installation + err = EUserCancelled; + } + break; + case SwiUI::KSWInstErrInsufficientMemory: + { + LOG( "Installer left: not enough space" ); + err = EInsufficientMemory; + } + break; + case SwiUI::KSWInstErrMissingDependency: + { + LOG( "Installer left: missing dependency." ); + //err = EInstallationFailed; + } + break; + case SwiUI::KSWInstErrFileCorrupted: + case SwiUI::KSWInstErrPackageNotSupported: + case SwiUI::KSWInstErrGeneralError: + case SwiUI::KSWInstErrNoRights: + case SwiUI::KSWInstErrNetworkFailure: + case SwiUI::KSWInstErrAccessDenied: + case SwiUI::KSWInstUpgradeError: + { + LOG2( "Installer left, err=%d", aError ); + //err = EInstallationFailed; + } + break; + case KErrServerTerminated: + { + LOG( "Server process terminated, cannot continue." ); + } + break; + case EUnsupportedDevice: + { + LOG( "MachineId not present in supported devices list" ); + err = EDeviceNotSupported; + } + break; + case KErrAlreadyExists: + { + // Download already exists + LOG( "Existing download" ); + err = EDownloadFailed; + } + break; + default: + { + LOG2( "Uncategorized leave %d, exiting", aError ); + } + break; + } + + LOG2( "Exiting due to failure %d", err ); + SetFailureReason( err ); + SetState( CStateFactory::EStatePrepareExitWithError ); + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CStateMachine::DoCancel() + { + LOG( "CStateMachine::DoCancel()" ); + iDownloadHandler->CancelAllDownloads(); + iInstallManager->CancelAll(); + } + +// ----------------------------------------------------------------------------- +// Sets the internet access point identifier for download handler +// ----------------------------------------------------------------------------- +// +void CStateMachine::SetIAP(TUint32 aIAP) + { + iDownloadHandler->SetIAP(aIAP); + } + +// ----------------------------------------------------------------------------- +// Handles the UI event from AppUi, filters it and passes it to states. +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleUserResponse(TInt aStatus) + { + switch (aStatus) + { + case EAknSoftkeyYes: + iState->UserAccepted(); + break; + case EAknSoftkeyNo: + iState->UserRejected(); + break; + case EAknSoftkeyCancel: + case EKeyPhoneEnd: + // fall-through + default: + iState->UserCancelled(); + break; + } + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleInstallCompletedL(TInt aStatus) + { + LOG4( "HandleInstallCompletedL(%d): %d. '%S'", aStatus, StateIndex(), iCurrentPackage->GetSisPackageName() ); + // TODO: Can iCurrentPackage ever be null? In case of root package installation?? + + iState->InstallCompleted( aStatus ); + } + +// ----------------------------------------------------------------------------- +// Called, if IAP has changed +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleIapChanged(const TUint32 aIAP) + { + iAppUi.iIAP = aIAP; + SetIAP(aIAP); + } + +// ----------------------------------------------------------------------------- +// Called when download has finished. +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleHttpFetchCompleted( + const TDesC& aDlFilename, + const TInt /* aDownloadType */, + const TInt32 aBytesDownloaded, + const TReal32 aAvgDlSpeed, // bytes/second + const TInt64 aDlTime // microseconds + ) + { + delete iDownloadedFile; + iDownloadedFile = NULL; + iDownloadedFile = aDlFilename.Alloc(); + iDownloadSize = aBytesDownloaded; + // Indicate that we've downloaded this amount already to keep progress bar happy + iCumulativeDownloaded += aBytesDownloaded; + // Calculate cumulative average download speed + iDownloadTime += aDlTime; + +#ifdef DEBUG_ADM + TReal32 avgDlSpeed = iCumulativeDownloaded*1000000.0 / iDownloadTime; + +#ifdef _DEBUG + TBuf<32> buf; + TReal r = avgDlSpeed; + TRealFormat fmt(3, 2); + fmt.iType = KRealFormatFixed; // | KDoNotUseTriads; + buf.AppendNum(r/1024.0, fmt); + buf.Append(' '); + buf.Append(iAppUi.iPrefixKb); + buf.Append('/'); + buf.Append('s'); + iAppUi.EikonEnv()->InfoMsg(buf); +#endif + + LOG8( "DL %7d/%7d (%.1f s, %3.02f kB/s) (total: %d kB, %.1f s, avg %.02f kB/s) OK", + aBytesDownloaded, aBytesDownloaded, aDlTime/1000000.0, aAvgDlSpeed/1024.0, + iCumulativeDownloaded/1024, iDownloadTime/1000000.0, avgDlSpeed/1024.0 ); +#endif + iState->DownloadCompleted(aBytesDownloaded); + } + +// ----------------------------------------------------------------------------- +// Called when download manager has retrieved a packet from the network. +// Required for updating the progress bar. +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleHttpFetchInProgress(const TInt32 aAlreadyDownloaded, const TInt32 aDownloadSize, const TReal32 aAvgDlSpeed) + { + iState->DownloadInProgress(aAlreadyDownloaded, aDownloadSize, aAvgDlSpeed); + } + +// ----------------------------------------------------------------------------- +// Called when download manager encounters a problem while downloading +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleHttpFetchFailure( + const TDesC& aDlFilename, + const TInt aGlobalErrorId, + const TInt aErrorId, + const TInt aFetchType + ) + { + if (aFetchType) + { + LOG5( "+ HandleHttpFetchFailure(): %d. %d, %d '%S'", StateIndex(), aGlobalErrorId, aErrorId, &aDlFilename ); + } + delete iDownloadedFile; + iDownloadedFile = NULL; + iDownloadedFile = aDlFilename.Alloc(); + // TODO: Filter the errors here and invoke proper callbacks in states + iState->DownloadFailed(); + LOG( "- HandleHttpFetchFailure()" ); + } + +// ----------------------------------------------------------------------------- +// Signals the semaphore that download deleting is in progress (and the installation +// can safely continue). +// ----------------------------------------------------------------------------- +// +void CStateMachine::HandleDownloadDeleting() + { + iDlDeletingSemaphore.Signal(); + } + +// ============================================================================= + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TState& CStateFactory::GetState(const TAppState aState) const + { + __ASSERT_DEBUG( aState < EStateLast, Panic(EPanicStateIndexOutOfBounds) ); + return *iStates[aState]; + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TInt CStateFactory::StateIndex(const TState* aState) const + { + TInt state; + for (state = 0; state < EStateLast; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + // We end up here, if the state is not set, which may happen, if this + // is called before CStateMachine::Start() or CStateMachine::SetState(). + return KUnknownState; + } + +// ----------------------------------------------------------------------------- +// Creates a new CStateFactory. +// @return Newly created CStateFactory. +// ----------------------------------------------------------------------------- +// +CStateFactory* CStateFactory::NewL(CStateMachine& aStateMachine) + { + CStateFactory* self = new (ELeave) CStateFactory(aStateMachine); + CleanupStack::PushL(self); + + // Create the states + self->iStates[EStateSetup] = new (ELeave) TStateSetup(*self); + self->iStates[EStateSetupParsing] = new (ELeave) TStateSetupParsing(*self); + self->iStates[EStateBuildDepTree] = new (ELeave) TStateBuildDepTree(*self); + self->iStates[EStateDownloadDepFile] = new (ELeave) TStateDownloadDepFile(*self); + self->iStates[EStateParseDepFile] = new (ELeave) TStateParseDepFile(*self); + self->iStates[EStateDownloadChangesFile] = new (ELeave) TStateDownloadChangesFile(*self); + self->iStates[EStateParseChangesFile] = new (ELeave) TStateParseChangesFile(*self); + self->iStates[EStateBuildFetchList] = new (ELeave) TStateBuildFetchList(*self); + self->iStates[EStateVerifyAvailableDiskSpace] = new (ELeave) TStateVerifyAvailableDiskSpace(*self); + self->iStates[EStateConfirmDownload] = new (ELeave) TStateConfirmDownload(*self); + self->iStates[EStateConfirmRoamingDownload] = new (ELeave) TStateConfirmRoamingDownload(*self); + self->iStates[EStateStartDependencyDownload] = new (ELeave) TStateStartDependencyDownload(*self); + self->iStates[EStateDownloadDependency] = new (ELeave) TStateDownloadDependency(*self); + self->iStates[EStateInstallDependency] = new (ELeave) TStateInstallDependency(*self); + self->iStates[EStateInstallAppSis] = new (ELeave) TStateInstallAppSis(*self); + self->iStates[EStateUninstallDependency] = new (ELeave) TStateUninstallDependency(*self); + self->iStates[EStatePrepareExitWithError] = new (ELeave) TStatePrepareExitWithError(*self); + self->iStates[EStateLaunchApp] = new (ELeave) TStateLaunchApp(*self); + self->iStates[EStateExit] = new (ELeave) TStateExit(*self); + + CleanupStack::Pop(); + + return self; + } + +// ----------------------------------------------------------------------------- +// Default constructor +// ----------------------------------------------------------------------------- +// +CStateFactory::CStateFactory(CStateMachine& aStateMachine) : + iStateMachine(aStateMachine) + { + } + +// ----------------------------------------------------------------------------- +// Default destructor +// ----------------------------------------------------------------------------- +// +CStateFactory::~CStateFactory() + { + // Deletes all state objects + iStates.DeleteAll(); + } + +// ============================================================================= + +TState::TState(const CStateFactory& aFactory) : + iFactory(aFactory), + iStateMachine(aFactory.StateMachine()) + { + } + +void TState::PanicInState(TStatePanic aPanic) const + { + Panic(aPanic, iFactory.StateIndex(this)); + } + +// Change of states +void TState::Enter() const + { + } + +void TState::Exit() const + { + } + +// Events from UI +void TState::UserAccepted() + { + LOG_FUNC + } + +// ----------------------------------------------------------------------------- +// Implements the default user cancel behaviour. If needed, overwrite in the +// state implementation. +// ----------------------------------------------------------------------------- +// +void TState::UserCancelled() + { + LOG_FUNC + iStateMachine.SetFailureReason(EUserCancelled); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + +// ----------------------------------------------------------------------------- +// Implements the default user reject behaviour. If needed, overwrite in the +// state implementation. +// ----------------------------------------------------------------------------- +// +void TState::UserRejected() + { + UserCancelled(); + } + +// Events from DownloadManager +void TState::DownloadInProgress(const TInt /* aAlreadyDownloaded */, const TInt /* aDownloadSize */, const TReal32 /* aAvgDlSpeed */) + { + //LOG_FUNC + } + +void TState::DownloadCompleted(const TInt /* aBytesDownloaded */) + { + LOG_FUNC + } + + void TState::DownloadFailed() + { + LOG_FUNC + } + +void TState::DownloadFailed(const CStateFactory::TAppState aState) + { + const EDownloadError errId = iStateMachine.iDownloadHandler->GetDownloadError(); + SLOG3( "+ DownloadFailed(): %d. err=%d", iStateMachine.StateIndex(), errId ); + + if ( (errId == EDlErrDownloadFailure ) && ( iStateMachine.iRestartAttempt < KDownloadRestartRetries) ) + { + iStateMachine.iRestartAttempt++; + SLOG2( "Download restart, attempt %d", iStateMachine.iRestartAttempt ); + iStateMachine.SetState( aState ); + } + else + { + SLOG3( "Aborting download. (%d; %d)", iStateMachine.iRestartAttempt, errId ); +// iStateMachine.iRestartAttempt = 0; + iStateMachine.SetFailureReason(EDownloadFailed); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + } + +void TState::DownloadCancelled() + { + LOG_FUNC + iStateMachine.SetFailureReason(EUserCancelled); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + +// Events from InstallManager +void TState::InstallInProgress() + { + LOG_FUNC + } + +void TState::InstallCompleted(TInt /* aStatus */) + { + LOG_FUNC + } + +void TState::InstallationCancelled() + { + LOG_FUNC + } + +// ============================================================================= +TStateSetup::TStateSetup(const CStateFactory& aFactory) : + TState(aFactory) + { + LOG_FUNC + } + +void TStateSetup::Enter() const + { + LOG_FUNC + + // TODO: Check if we are roaming and proceed according to that + + iStateMachine.SetState( CStateFactory::EStateSetupParsing ); + } + +TStateSetupParsing::TStateSetupParsing(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateSetupParsing::Enter() const + { + const TInt mUid = iStateMachine.MachineUid(); + if ( mUid ) + { + iStateMachine.iDepTree->SetMachineId( mUid ); + } + else + { + PanicInState( EPanicStateInvalidMachineUid ); + } + + // Get the drive where wrapper package was installed to + const TBool driveStatus = iStateMachine.iInstallManager->GetPackageInstalledDriveL( + iStateMachine.WrapperPackageUid(), + iStateMachine.iWrapperInstalledDrive); + + +#ifdef __WINS__ + iStateMachine.iWrapperInstalledDrive = 'C'; +#else + if ( !driveStatus ) + { + // Read the wrapper installed drive from resume info file. + // TODO: What if user has tried to install on a removable drive and it's not available during the menu launch? + } +#endif + + SLOG3( "Installer pkg on %c: (%d)", (char)iStateMachine.iWrapperInstalledDrive, driveStatus ); + + // Cannot combine this with drive status. User might be trying to reinstall an app, already present in the device! + TVersion version, minVersion = KMinBootstrapSisVersion; + if ( iStateMachine.iInstallManager->GetPackageVersionInfoL(KUidBootstrap, version) ) + { + // Compare installed and min required Bootstrap versions + if ( CompareVersions(minVersion, version) == EGreaterFirstVersion ) + { + iStateMachine.SetFailureReason( EInvalidBootstrapVersion ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + return; + } + } + else + { + // Launched from the menu grid + } + + switch ( iStateMachine.BootstrapVersion() ) + { + case 1: + case 2: + case 3: + { + // Parses the dependency file included withing the wrapper package. + SLOG2( "* Parsing '%S'", iStateMachine.DepFilename() ); + iStateMachine.iXmlParser->GetDepContentsL( + *iStateMachine.DepFilename(), + iStateMachine.iCurrentPackage, + iStateMachine.iDepTree + ); + iStateMachine.SetState( CStateFactory::EStateBuildDepTree ); + } + break; + case 4: + { + // Parses the application.sis file included in the wrapper package. + SLOG2( "* Parsing '%S'", iStateMachine.SisFilename() ); + iStateMachine.iSisParser->GetDepContentsL( + *iStateMachine.SisFilename(), + iStateMachine.iCurrentPackage, + iStateMachine.iDepTree + ); + iStateMachine.SetState( CStateFactory::EStateBuildDepTree ); + } + break; + default: + { + SLOG2( "Invalid bootstrap v%d", iStateMachine.BootstrapVersion() ); + iStateMachine.SetFailureReason( EInvalidBootstrapVersion ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + break; + } + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateParseDepFile::TStateParseDepFile(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateParseDepFile::Enter() const + { + SLOG2( "+ ParseDepFileL(): '%S'", iStateMachine.iDownloadedFile ); + iStateMachine.iXmlParser->GetDepContentsL( + *iStateMachine.iDownloadedFile, + iStateMachine.iCurrentPackage, + iStateMachine.iDepTree + ); + iStateMachine.SetState( CStateFactory::EStateBuildDepTree ); + SLOG2( "- ParseDepFileL(): '%S'", iStateMachine.iDownloadedFile ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateParseChangesFile::TStateParseChangesFile(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateParseChangesFile::Enter() const + { + SLOG2("+ ParseChangesFileL(): '%S'", iStateMachine.iDownloadedFile); + // Parse the provided changes file. + // the parameter aChangesFileName will be invalid after the call + iStateMachine.iXmlParser->GetChangesInfoL( + *iStateMachine.iDownloadedFile, + iStateMachine.iCurrentPackage, + iStateMachine.iDepTree + ); + iStateMachine.SetState( CStateFactory::EStateDownloadDepFile ); + SLOG2( "- ParseChangesFileL(): '%S'", iStateMachine.iDownloadedFile ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateBuildFetchList::TStateBuildFetchList(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateBuildFetchList::Enter() const + { + // Construct the list of dependent packages + SLOG( "Building dependency list" ); + iStateMachine.iDepTree->ConstructFetchListL(); + + // Remove all downloaded dep files + SLOG( "Removing downloaded files" ); + iStateMachine.iDepTree->RemoveDownloadedFiles( iStateMachine.FsSession() ); + + // Check if cycle exists + if ( iStateMachine.iDepTree->IsCyclePresent() ) + { + // TODO: Inform user about configuration failure, exit gracefully + SLOG( "! Failure: cycle exists in the graph." ); + iStateMachine.SetFailureReason( EInstallationFailed ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + iStateMachine.SetState( CStateFactory::EStateVerifyAvailableDiskSpace ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateVerifyAvailableDiskSpace::TStateVerifyAvailableDiskSpace(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateVerifyAvailableDiskSpace::Enter() const + { + // MaxDownloadSize is subtracted from available System drive space. + + // In case of silent installation of main app, if there is no space available in the drive selected by the user for wrapper, + // normal installation to follow. + + // Set the drive info + if ( iStateMachine.iDepTree->SetDriveInfo() ) + { + // With Ovi Store prompt always + const TInt dlThreshold = iStateMachine.OviStoreRunning() + ? 0 + : KDownloadPromptThreshold; + const TInt dlSize = iStateMachine.iDepTree->GetTotalDownloadSize(); + + SLOG3( "Download size %d (%d)", dlSize, dlThreshold ); + +#ifdef DEBUG_ADM + // Print all the drives set. To be removed after testing. + CPackageInfo *node = NULL; + while( (node = iStateMachine.iDepTree->GetNextSortedNode()) != NULL ) + { + SLOG5( "PkgUid 0x%08X: InstDrive %c:, prio %d, size %d", + node->GetPackageUid(), (char)node->GetInstallDrive(), + node->GetDrivePriority(), node->GetInstallSize() ); + } +#endif + // Show user how much to download and confirm, if he/she wants to do that + // if we have dependencies to download at all + if ( dlSize > 0 ) + { + // Finalize the progress bar + iStateMachine.UpdateProgressBar( KProgressBarFullValue, KProgressBarFullValue ); + + // We need to download dependencies + if ( iStateMachine.UsingWLAN() && !iStateMachine.OviStoreRunning() ) + { + // If using WLAN, start the dependency download immediately without prompting the user + iStateMachine.SetState( CStateFactory::EStateStartDependencyDownload ); + } + else if ( iStateMachine.RegisteredRoaming() ) + { + // If the device is roaming, always prompt the user + iStateMachine.SetState( CStateFactory::EStateConfirmRoamingDownload ); + } + else if ( dlSize > dlThreshold ) + { + // If not using WLAN and the download threshold has exceeded, prompt the user + iStateMachine.SetState( CStateFactory::EStateConfirmDownload ); + } + else + { + // Otherwise, continue the download automatically without prompting the user + iStateMachine.SetState( CStateFactory::EStateStartDependencyDownload ); + } + } + else + { + SLOG( "Nothing to download, dependencies OK." ); + // Keep the next progress bar update happy + iStateMachine.iProgressMaxValue = KProgressBarFullValue; + // Finalize the progress bar + iStateMachine.UpdateProgressBar( KProgressBarFullValue*95/100, KProgressBarFullValue ); + iStateMachine.SetState( CStateFactory::EStateInstallAppSis ); + } + } + else + { + iStateMachine.SetFailureReason( EInsufficientMemory ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + } + +// ----------------------------------------------------------------------------- +// Builds the dependency tree. This function controls the state machine. +// ----------------------------------------------------------------------------- +// +TStateBuildDepTree::TStateBuildDepTree(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateBuildDepTree::Enter() const + { + SLOG2( "+ BuildDepTreeL(%d)", iStateMachine.iNodesProcessed ); + + // Update progress bar + iStateMachine.iNodesProcessed++; + + iStateMachine.iCurrentPackage = iStateMachine.iDepTree->GetNextNode(); + CPackageInfo* package = iStateMachine.iCurrentPackage; + + if ( package == NULL ) + { + SLOG( "Last package processed" ); + // Do the final progress bar update + iStateMachine.UpdateProgressBar( KProgressBarFullValue*95/100, KProgressBarFullValue ); + + iStateMachine.SetState( CStateFactory::EStateBuildFetchList ); + } + else + { + SLOG3( "* Processing 0x%08X (%d)", package->GetPackageUid(), package->GetPackageStatus() ); + + iStateMachine.UpdateProgressBar( iStateMachine.iNodesProcessed, + iStateMachine.iNodesProcessed+2 ); + + // Call the install class api to validate and set the package status + TUid uid = TUid::Uid( package->GetPackageUid() ); + TVersion version, test; + + test = package->GetPackageVersion(); + + if (iStateMachine.iInstallManager->GetPackageVersionInfoL(uid, version)) + { + // Compare installed and required verions + // to find if upgrade is required. + if ( (CompareVersions(test, version) ) == EGreaterFirstVersion) + { + SLOG8( "Package 0x%08X: upgrade %d.%02d.%d -> %d.%02d.%d", + uid.iUid, + version.iMajor, version.iMinor, version.iBuild, + test.iMajor, test.iMinor, test.iBuild + ); + package->SetPackageStatus(EPackageToBeUpgraded); + } + else + { + SLOG5( "Package 0x%08X: v%d.%02d.%d already installed", uid.iUid, + version.iMajor, version.iMinor, version.iBuild + ); + package->SetPackageStatus(EPackageInstalled); + } + } + else + { + SLOG5( "Package 0x%08X: v%d.%02d.%d to be downloaded", uid.iUid, + test.iMajor, test.iMinor, test.iBuild + ); + package->SetPackageStatus(EPackageToBeFetched); + } + + iStateMachine.SetState( CStateFactory::EStateDownloadChangesFile ); + } + SLOG2( "- BuildDepTreeL(%d)", iStateMachine.iNodesProcessed ); + } + +// ----------------------------------------------------------------------------- +// Requests a download for the dependecy sis package _dep.xml file. +// ----------------------------------------------------------------------------- +// +TStateDownloadDepFile::TStateDownloadDepFile(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateDownloadDepFile::Enter() const + { + //Here fetch the dep file of the current package. + SLOG2( "+ FetchDepFileL(): 0x%08X", iStateMachine.iCurrentPackage->GetPackageUid() ); + + // TODO:Here the download Url should never be null] + // as its mandatory to specify the Url in changes file. + TPtrC8 urlPtr(NULL, 0); + if (iStateMachine.iCurrentPackage->GetDownloadUrlL() != NULL) + { + SLOG8_2( "DepPkg URL: '%S'", iStateMachine.iCurrentPackage->GetDownloadUrlL() ); + urlPtr.Set(iStateMachine.iCurrentPackage->GetDownloadUrlL()->Des() ); + } + else + { + SLOG8_2( "DepPkg URL: '%S' (default)", iStateMachine.ConfigUrl() ); + urlPtr.Set( *iStateMachine.ConfigUrl() ); + } + + User::LeaveIfNull( iStateMachine.iCurrentPackage->GetDepFileName() ); + + SLOG2( "Downloading DEP: '%S'", iStateMachine.iCurrentPackage->GetDepFileName() ); + + iStateMachine.iDownloadHandler->StartDownloadL( + urlPtr, + *iStateMachine.iCurrentPackage->GetDepFileName(), + iStateMachine.iDepTree->GetDownloadPath(), + 1); + // We don't request a new state after this. When download completes + // succesfully, a new state change will be requested. + } + +void TStateDownloadDepFile::DownloadCompleted(const TInt /* aBytesDownloaded */) + { + iStateMachine.iRestartAttempt = 0; + iStateMachine.SetState( CStateFactory::EStateParseDepFile ); + } + +void TStateDownloadDepFile::DownloadFailed() + { + LOG_FUNC + TState::DownloadFailed( CStateFactory::EStateDownloadDepFile ); + } + +// ----------------------------------------------------------------------------- +// Requests a download for the _changes.xml file. +// ----------------------------------------------------------------------------- +// +TStateDownloadChangesFile::TStateDownloadChangesFile(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateDownloadChangesFile::Enter() const + { + // See if the URL is provided as part of the + // dep file, otherwise use the default one. + TPtrC8 urlPtr(NULL, 0); + if ( iStateMachine.iCurrentPackage->GetURL() != NULL ) + { + SLOG8_2( "Changes URL: '%S'", iStateMachine.iCurrentPackage->GetURL() ); + urlPtr.Set(iStateMachine.iCurrentPackage->GetURL()->Des()); + } + else + { + SLOG8_2( "Changes URL: '%S' (default)", iStateMachine.ConfigUrl() ); + urlPtr.Set( *iStateMachine.ConfigUrl() ); + } + + //TODO: The only reason why this is back here : + //Changes file name for Root is not set when root is parsed. Fix it. + iStateMachine.iCurrentPackage->SetChangesFileNameL(); + SLOG2( "Downloading CHANGES: '%S'", iStateMachine.iCurrentPackage->GetChangesFileName() ); + + // Download the changes file + iStateMachine.iDownloadHandler->StartDownloadL( + urlPtr, + *iStateMachine.iCurrentPackage->GetChangesFileName(), + iStateMachine.iDepTree->GetDownloadPath(), + 0); + // We don't request a new state after this. When download completes + // succesfully, a new state change will be requested by DownloadCompleted() + } + +void TStateDownloadChangesFile::DownloadCompleted(const TInt /*aBytesDownloaded*/) + { + LOG_FUNC + + iStateMachine.iNodesProcessed++; + iStateMachine.UpdateProgressBar( iStateMachine.iNodesProcessed, iStateMachine.iNodesProcessed+2 ); + + iStateMachine.iRestartAttempt = 0; + iStateMachine.SetState( CStateFactory::EStateParseChangesFile ); + } + +void TStateDownloadChangesFile::DownloadFailed() + { + LOG_FUNC + TState::DownloadFailed( CStateFactory::EStateDownloadChangesFile ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateStartDependencyDownload::TStateStartDependencyDownload(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateStartDependencyDownload::Enter() const + { + const TUint32 totalDlSize = iStateMachine.iDepTree->GetTotalDownloadSize(); + SLOG2( "Starting to download %d bytes", totalDlSize ); + + // Remove all downloads before proceeding + iStateMachine.iDownloadHandler->CancelAllDownloads(); + + // Start progress bar from beginning + iStateMachine.iCounterCurrent = 1; + iStateMachine.iCounterMax = iStateMachine.iDepTree->CountDownloadNodes(); + + // reset to zero for download and install of packages. + iStateMachine.iDownloadSize = 0; + iStateMachine.iNodesProcessed = 0; + + // Start the dependency download! + iStateMachine.SetState( CStateFactory::EStateDownloadDependency ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateDownloadDependency::TStateDownloadDependency(const CStateFactory& aFactory) : + TState(aFactory), + iRetryDownload(EFalse) + { + } + +void TStateDownloadDependency::Enter() const + { + SLOG3( "+ FetchPackageL(%d): retry=%d", iStateMachine.iNodesProcessed, iRetryDownload ); + + CPackageInfo* fetchPackage = NULL; + + if ( iRetryDownload ) + { + fetchPackage = iStateMachine.iDepTree->GetCurrentFetchNode(); + } + else + { + iStateMachine.iNodesProcessed++; + // always ensure that iCurrentPackage points to the correct one + fetchPackage = iStateMachine.iDepTree->GetNextFetchNode(); + } + iStateMachine.iCurrentPackage = fetchPackage; + + if ( !fetchPackage ) + { +/* + //end of list - we're finished + SLOG2( "Uninstalling wrapper 0x%08X", iStateMachine.WrapperPackageUid().iUid ); + TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( + iStateMachine.WrapperPackageUid() ); + SLOG2( "Uninstall status: %d", err); +*/ + // Wrapper has been uninstalled, ready to exit/launch + iStateMachine.SetState( CStateFactory::EStateExit ); + return; + } +/* + // Attempting to fix 'Network connection lost!' errors on 3G. Does not help. + SLOG( "Deleting all downloads" ); + iStateMachine.iDownloadHandler->CancelAllDownloads(); +*/ + switch ( fetchPackage->GetPackageStatus() ) + { + case EPackageRootToBeInstalled: + { + //SLOG( "* Installing AppSis" ); + + iStateMachine.SetState( CStateFactory::EStateInstallAppSis ); + } + break; + case EPackageToBeFetched: + case EPackageToBeUpgraded: + { + SLOG2( "* Package 0x%08X to be Fetched/Upgraded", fetchPackage->GetPackageUid() ); + + // Initialize progress bar + //ShowGlobalProgressL(R_ADM_DOWNLOAD_WAIT_TEXT, 0, KMaxTInt); + + if ( iStateMachine.iCounterMax > 0 ) + { + iStateMachine.StopWaitNote(); + // prevent progress bar from starting from previous dl size + iStateMachine.iProgressCurrentValue = 0; + iStateMachine.iCumulativeDepDownloaded = 0; + iStateMachine.iProgressMaxValue = fetchPackage->GetDownloadSize(); + iStateMachine.ShowProgressBarL( + 0, + iStateMachine.iProgressMaxValue, + iStateMachine.iCounterCurrent, + iStateMachine.iCounterMax + ); + } + + // TODO:Here the download Url should never be null, + // as its mandatory to specify the Url in changes file. + TPtrC8 urlPtr(NULL, 0); + if (fetchPackage->GetDownloadUrlL() != NULL ) + { + SLOG8_2( "Package URL: '%S'", fetchPackage->GetDownloadUrlL()); + urlPtr.Set(fetchPackage->GetDownloadUrlL()->Des()); + } + else + { + SLOG8_2( "Package URL: '%S' (default)", iStateMachine.ConfigUrl() ); + urlPtr.Set(*iStateMachine.ConfigUrl()); + } + + User::LeaveIfNull( fetchPackage->GetSisPackageName() ); + + SLOG2( "Downloading: '%S'", fetchPackage->GetSisPackageName() ); + iStateMachine.iDownloadHandler->StartDownloadL( + urlPtr, + *fetchPackage->GetSisPackageName(), + iStateMachine.iDepTree->GetDownloadPath(), + 2); + } + break; + case EPackageInstalled: + { + SLOG2( "* Package 0x%08X installed", fetchPackage->GetPackageUid()); + //FetchPackageL(); + iStateMachine.SetState( CStateFactory::EStateDownloadDependency ); + } + break; + default: + { + // TODO: Better error handling + Panic( EPanicStateFetchStatus ); + } + break; + } // switch + SLOG2( "- FetchPackageL(%d)", iStateMachine.iNodesProcessed ); + } + +void TStateDownloadDependency::DownloadCompleted(const TInt aBytesDownloaded) + { + iRetryDownload = EFalse; + // Indicate that we've downloaded this amount already to keep progress bar happy + iStateMachine.iCumulativeDepDownloaded += aBytesDownloaded; + iStateMachine.iDownloadSize = iStateMachine.iCumulativeDepDownloaded; + iStateMachine.SetState( CStateFactory::EStateInstallDependency ); + } + +void TStateDownloadDependency::DownloadInProgress(const TInt aAlreadyDownloaded, const TInt aDownloadSize, const TReal32 aAvgDlSpeed) + { + static TInt32 prevAlready = 0; + // Prevents log and event flooding + if (aAlreadyDownloaded != prevAlready) + { + prevAlready = aAlreadyDownloaded; + + iStateMachine.iDownloadSize = aAlreadyDownloaded + iStateMachine.iCumulativeDepDownloaded; + iStateMachine.iProgressCurrentValue = + iStateMachine.iCumulativeDepDownloaded + aAlreadyDownloaded; + if (aAvgDlSpeed > 0) + { + SLOG4( "DL %7d/%7d (%3.02f kB/s)", aAlreadyDownloaded, aDownloadSize, aAvgDlSpeed/1024 ); + } + else + { + SLOG3( "DL %7d/%7d", aAlreadyDownloaded, aDownloadSize ); + } + // Update the progress bar + iStateMachine.UpdateProgressBar( iStateMachine.iProgressCurrentValue, + iStateMachine.iProgressMaxValue ); + } + } + +void TStateDownloadDependency::DownloadFailed() + { + LOG_FUNC + iRetryDownload = ETrue; + TState::DownloadFailed( CStateFactory::EStateDownloadDependency ); + } + +// ----------------------------------------------------------------------------- +// Uninstalls installed dependencies one at the time +// ----------------------------------------------------------------------------- +// +TStateUninstallDependency::TStateUninstallDependency(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateUninstallDependency::Enter() const + { + CPackageInfo* node = iStateMachine.iDepTree->GetPreviousFetchNode(); + // Uninstall all the fetched and installed dependencies + if (node) + { + // TODO: Can the package present check be moved to Install handler?. Below is an async function though. + if ( (node->GetPackageStatus() == EPackageFetchedInstalled) && + ( iStateMachine.iInstallManager->IsPackagePresentL(TUid::Uid(node->GetPackageUid())) ) ) + { + SLOG2( "Uninstalling: 0x%x", node->GetPackageUid() ); + + iStateMachine.iInstallManager-> + SilentUninstallPackageAsync( TUid::Uid(node->GetPackageUid()) ); + // the next event will be triggered by InstallCompleted() below + } + else + { + SLOG2( "Skipping: 0x%x", node->GetPackageUid() ); + // Process the next package + iStateMachine.SetState( CStateFactory::EStateUninstallDependency ); + } + } + else + { + SLOG( "About to uninstall wrapper" ); + // We've processed the dependency tree, uninstall the wrapper + // Failure reason is set before calling EPrepareForExitWithError + iStateMachine.SetState( CStateFactory::EStateExit ); + } + } + +// This is actually handling the UNinstallation completed event +void TStateUninstallDependency::InstallCompleted(TInt aStatus) + { + SLOG2( "UninstallDependencyCompleted(): %d", aStatus ); + // Try to uninstall next dependency, without minding the status code + iStateMachine.SetState( CStateFactory::EStateUninstallDependency ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateLaunchApp::TStateLaunchApp(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateLaunchApp::Enter() const + { + RArray appSids; + TBool launchStatus = ETrue; + // Get the SID list from installer + TUid uid = TUid::Uid( iStateMachine.iCurrentPackage->GetPackageUid() ); + const TBool status = iStateMachine.iInstallManager->GetPackageSidsL(uid, appSids); + + if ( status ) + { +#ifdef DEBUG_ADM + for (TInt i = 0; i < appSids.Count(); i++) + { + SLOG3( "SID[%d]=0x%08X", i, appSids[i] ); + } +#endif + TApaAppInfo appInfo; + RApaLsSession lsSession; + + // Connect to Apparc + if ( !lsSession.Connect() ) + { + CleanupClosePushL( lsSession ); + + for (TInt i = 0; i < appSids.Count(); i++) + { + TInt ret = lsSession.GetAppInfo( appInfo, appSids[i] ); + // Get the app Info of the Sid. + if ( ret == KErrNone ) + { + HBufC* iconFilename = NULL; + // Get the application icon, if any. It's used in the launch dialog. + ret = lsSession.GetAppIcon( appSids[i], iconFilename ); + if (ret == KErrNone) + { + // We have a launchable exe and icon: get the executable name and caption + iStateMachine.iLaunchName = appInfo.iFullName; + iStateMachine.iLaunchCaption = appInfo.iCaption; + iStateMachine.iLaunchIconFilename.Copy( *iconFilename ); + SLOG5( "LaunchExe %d '%S' '%S' '%S'", i, &iStateMachine.iLaunchName, &iStateMachine.iLaunchCaption, &iStateMachine.iLaunchIconFilename ); + + delete iconFilename; + break; + } + // The .exe didn't have an icon, try next .exe. + // We're assuming that only exe's having an icon are the ones we should launch + } // if + } // for + CleanupStack::PopAndDestroy( &lsSession ); + } //if ( !lsSession.Connect() ) + else + { + launchStatus = EFalse; + } + } // if( status ) + else + { + launchStatus = EFalse; + } + + appSids.Close(); + + if ( launchStatus && iStateMachine.iLaunchName.Length() ) + { + iStateMachine.SetAppLaunch(ETrue); + iStateMachine.iAppUi.ShowLaunchPromptL( iStateMachine.iLaunchCaption, iStateMachine.iLaunchIconFilename ); + } + else + { + // No executable to launch! + iStateMachine.SetState( CStateFactory::EStateExit ); + } + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void TStateLaunchApp::UserAccepted() + { + RApaLsSession lsSession; + + // Connect to Apparc + User::LeaveIfError( lsSession.Connect() ); + CleanupClosePushL( lsSession ); + + CApaCommandLine* cmdLine = CApaCommandLine::NewLC(); + cmdLine->SetExecutableNameL( iStateMachine.iLaunchName ); + cmdLine->SetCommandL( EApaCommandRun ); + + SLOG3( "Launching %S '%S'", &iStateMachine.iLaunchCaption, &iStateMachine.iLaunchName ); + // Launch the application + // TODO: Better error handling + User::LeaveIfError( lsSession.StartApp( *cmdLine ) ); + + CleanupStack::PopAndDestroy( 2, &lsSession ); // cmdLine, lsSession + + iStateMachine.SetState( CStateFactory::EStateExit ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void TStateLaunchApp::UserRejected() + { + SLOG( "User denied application launch" ); + iStateMachine.SetAppLaunch(EFalse); + iStateMachine.SetState( CStateFactory::EStateExit ); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateExit::TStateExit(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateExit::Enter() const + { + SLOG2( "Exit (%d)", iStateMachine.FailedState() ); + +#ifdef FEATURE_INSTALL_RESUME + // FailedState is checked to differentiate a completion of successful menu launch from failure cases. + // Also uninstall the wrapper, when the resume information file is not required + // (in case of Bootstrap failure mode - KCmdLineCancelAdmNoResume) + if ( ( iStateMachine.FailedState() == CStateFactory::EStateLast ) || + ( iStateMachine.FailedState() == KUnknownState ) || + ( !iStateMachine.ResumeRequired() ) + ) + { + // Do the final cleanup here: remove the wrapper package, which + // will also remove the bootstrap + SLOG2( "Uninstalling wrapper 0x%08X", iStateMachine.WrapperPackageUid().iUid ); + const TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( + iStateMachine.WrapperPackageUid() ); + if (err != KErrNone) + { + SLOG2( "! Uninstall failed (%d)", err ); + } + + // Remove resume info files + RemoveResumeFiles(); + } + else + { + // Remove only the bootstrap. + // Wrapper is retained for Menu launch. + // It is possible that bootstrap is already removed in previous unsuccessful menu launch + if ( iStateMachine.iInstallManager->IsPackagePresentL( KUidBootstrap ) ) + { + SLOG2( "Uninstalling bootstrap 0x%08X", KUidBootstrap ); + const TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( KUidBootstrap ); + // ignore any errors + if (err != KErrNone) + { + SLOG2( "! Uninstall failed (%d)", err ); + } + } + } +#else + // Do the final cleanup here: remove the wrapper package + SLOG2( "Uninstalling wrapper 0x%08X", iStateMachine.WrapperPackageUid().iUid ); + const TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( + iStateMachine.WrapperPackageUid() ); + if (err != KErrNone) + { + SLOG2( "! Uninstall failed (%d)", err ); + } +#endif // FEATURE_INSTALL_RESUME + +#ifdef FEATURE_REPORTING + // Report only the specified cases + switch ( iStateMachine.ExitReason() ) + { + case EExitNoError: + case EUserCancelled: +// case EDownloadFailed: + case EInsufficientMemory: + case EDeviceNotSupported: + case EInstallationFailed: + case EInvalidBootstrapVersion: + case ERootInstallationFailed: + Report(); + break; + default: + break; + } +#else + // We can exit the application + // The default failure reason is "No Error" + iStateMachine.iAppUi.ExitApp(); + + // Above call does not return +#endif + } + +// ----------------------------------------------------------------------------- +// RemoveResumeFiles() +// On successful completion of the installation process, remove the resume info file. +// ----------------------------------------------------------------------------- +// +void TStateExit::RemoveResumeFiles() const + { + if ( BaflUtils::FileExists(iStateMachine.FsSession(), KADMResumeInfoFile) ) + { + RArray< TPtrC > lineBuffer; + TInt ret = 0; + HBufC* buffer = ReadConfigFile( iStateMachine.FsSession(), KADMResumeInfoFile, lineBuffer, ret ); + + if ( buffer && ret == KErrNone ) + { + // Get AppFileName from the config file + TPtrC appFileNameLine = GetConfigValue( KRFieldAppFileName, lineBuffer, ret ); + if ( ret == KErrNone ) + { + SLOG2( "AppFileName = '%S'", &appFileNameLine ); + + if ( BaflUtils::FileExists(iStateMachine.FsSession(), appFileNameLine) ) + { + const TInt err = iStateMachine.FsSession().Delete( appFileNameLine ); + SLOG2( "AppFile has been deleted (%d)", err); + } + } + else + { + SLOG2( "Failed to read value from resume info (%d)", ret ); + } + delete buffer; + } + ret = iStateMachine.FsSession().Delete( KADMResumeInfoFile ); + SLOG2( "Resume info has been deleted (%d)", ret ); + + // TODO: Need to remove the copied dep file as well + } + } +#ifdef FEATURE_REPORTING +// ----------------------------------------------------------------------------- +// Creates a report about the installation and sends that to the server +// ----------------------------------------------------------------------------- +// +void TStateExit::Report() const + { + _LIT8(KFmtVersion, "%d.%02d.%d"); + _LIT(KOK, "OK"); + TBuf8 url; + url.Copy( *iStateMachine.ConfigUrl() ); + + // ----------------------------------------------------------------------------- + // The base report URL is: + // http://server/root/x.yy.z/reason/flags/OK + // + // where + // + // http://server/root/ ConfigUrl() + // x.yy.z ADM version formatted as %d.%02d.%d + // reason iExitReason + // flags Flags defined below in Report Version 1 + // OK Non-existing file + // + // ----------------------------------------------------------------------------- + // Report Version 1 + // + //31 11 10 9 8 7 6 5 4 3 2 1 0 + // +-------+--+--+--+--+--+--+--+--+--+--+--+--+ + // |reservd| | | | | + // +-------+--+--+--+--+--+--+--+--+--+--+--+--+ + // + // value + // bits range description + // 0-3 0-15 Report version + // + // Other bits for version 1 + // + // value + // bits range description + // 4-9 0-63 Number of packages downloaded + // 10 1 Ovi Store running + // 11 1 Application launched + // ----------------------------------------------------------------------------- + // + + // Get the ADM version + TVersion version; + iStateMachine.iInstallManager->GetPackageVersionInfoL(KUidADMApp, version); + +#if 0 + const TUint32 KReportVersion = 1; + TUint32 flags; + flags = KReportVersion << 0; // 4 bits (0-15) + flags |= (iStateMachine.iDepTree->CountDownloadNodes() & 0x3f) << 4; // 6 bits (0-63) + flags |= iStateMachine.OviStoreRunning() << 10; // 1 bit + flags |= iStateMachine.AppLaunch() << 11; // 1 bit + SLOG2( "Exit: 0x%08x", flags ); +#endif + + url.AppendFormat(KFmtVersion, version.iMajor, version.iMinor, version.iBuild); + url.Append('/'); + url.AppendNum( iStateMachine.ExitReason() ); + url.Append('/'); + url.AppendNum( iStateMachine.OviStoreRunning() ); + url.Append('/'); + url.AppendNum( iStateMachine.ResumingInstallation() ); + url.Append('/'); +#if 0 + url.AppendNumFixedWidthUC(flags, EHex, 8); + url.Append('/'); + url.AppendNum( iStateMachine.OviStoreRunning() ); + url.Append('/'); + url.AppendNum( iStateMachine.AppLaunch() ); + url.Append('/'); +#endif + iStateMachine.iDownloadHandler->StartDownloadL( + url, + KOK, + iStateMachine.iDepTree->GetDownloadPath(), + 0); + } + +// ----------------------------------------------------------------------------- +// Ignore "in progress" event calls. +// ----------------------------------------------------------------------------- +// +void TStateExit::DownloadInProgress(const TInt /* aAlreadyDownloaded */, const TInt /* aDownloadSize */, const TReal32 /* aAvgDlSpeed */) + { + } + +// ----------------------------------------------------------------------------- +// Transfer control to ExitApp(). +// ----------------------------------------------------------------------------- +// +void TStateExit::DownloadCompleted(const TInt /* aBytesDownloaded */) + { + iStateMachine.iAppUi.ExitApp(); + } + +// ----------------------------------------------------------------------------- +// Transfer control to ExitApp(). +// ----------------------------------------------------------------------------- +// +void TStateExit::DownloadFailed() + { + iStateMachine.iAppUi.ExitApp(); + } + +// ----------------------------------------------------------------------------- +// Transfer control to ExitApp(). +// ----------------------------------------------------------------------------- +// +void TStateExit::DownloadCancelled() + { + iStateMachine.iAppUi.ExitApp(); + } +#endif + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStatePrepareExitWithError::TStatePrepareExitWithError(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStatePrepareExitWithError::Enter() const + { + // Show the user a wait note, if needed + iStateMachine.iAppUi.HandleFailure(); + + // TODO: FIX THIS! DlMgr requires some time for its AOs to do the cleanup. + User::After(1000000); + iStateMachine.iDownloadHandler->CancelAllDownloads(); + iStateMachine.iDepTree->RemoveDownloadedFiles( iStateMachine.FsSession() ); + +#ifdef FEATURE_INSTALL_RESUME + // Backup the resume info for future launch + if ( iStateMachine.ResumeRequired() ) + { + // Create the resume information + if ( CreateInstallationResumeInfo() == KErrNone ) + { + // Dependencies are not uninstalled if launched from Menu + iStateMachine.SetState( CStateFactory::EStateExit ); + return; + } + } + // If no resume info required or CreateInstallationResumeInfo fails. + iStateMachine.SetState( CStateFactory::EStateUninstallDependency ); +#else + // Trigger the dependency uninstallation + iStateMachine.SetState( CStateFactory::EStateUninstallDependency ); +#endif // FEATURE_INSTALL_RESUME + } + +// ----------------------------------------------------------------------------- +// CreateInstallationResumeInfo +// Backup the necessary information to continue the installation later i.e. +// from the application menu grid. +// ----------------------------------------------------------------------------- +// +#define CHECK(a) { \ + if (a != KErrNone) \ + goto failed; \ + pos++; \ + } + +TInt TStatePrepareExitWithError::CreateInstallationResumeInfo() const + { + SLOG( "+ CreateInstallationResumeInfo()" ); + + // This is increment in each CHECK() macro + TInt pos = 0; + + // Step 0: Backup the required files to ADM Private folder. + // (Also the names are written to resume info file) + TRAPD( err, BackupFilesL() ); + CHECK(err); + + RFile outFile; + CPackageInfo* node = NULL; + + TInt numberOfNodes = iStateMachine.iDepTree->NodesCount(); + // Skip the main application + if (numberOfNodes > 0) + { + numberOfNodes--; + } + + err = outFile.Open( iStateMachine.FsSession(), KADMResumeInfoFile, EFileWrite | EFileStreamText | EFileShareAny ); + CHECK(err); + + // Step 1: Write the application name + node = iStateMachine.iDepTree->GetRootNode(); + SLOG3( "Saving state for '%S' (%d)", node->GetPackageName(), numberOfNodes ); + err = WriteToConfigFile( outFile, KRFieldAppName, *node->GetPackageName() ); + CHECK(err); + + // Step 2: Write the depPkg UIDs to resume info file. + // A new resume info file is already created as part of the "BackupFilesL()" + // If user presses cancel before the fetchlist is constructed, then this loop will not execute. + for (TInt i = 0; i < numberOfNodes; i++) + { + node = iStateMachine.iDepTree->GetFetchNode( i ); + SLOG2( "Adding info: 0x%08X", node->GetPackageUid() ); + + err = WriteIntToConfigFile( outFile, KRFieldDepPkgUid, node->GetPackageUid() ); + CHECK(err); + } + +#if 0 + node = iStateMachine.iDepTree->GetRootNode(); + SLOG2( "Adding main: 0x%08X", node->GetPackageUid() ); + + // Step 3: Write the Root pkg UID to resume info file. + // TODO: Check whether this is really needed. + err = WriteIntToConfigFile( outFile, KRootPkgUid, node->GetPackageUid() ); + CHECK(err); +#endif + + // Step 4: Write the BootstrapVersion to resume info file. + err = WriteIntToConfigFile( outFile, KRFieldBootstrap, iStateMachine.BootstrapVersion() ); + CHECK(err); + + // Step 5: Write the Wrapper installed drive to resume info file. + err = WriteIntToConfigFile( outFile, KRFieldWrapperDrive, iStateMachine.iWrapperInstalledDrive ); + CHECK(err); + + // Step 6: Write the Wrapper Package UID. + err = WriteIntToConfigFile( outFile, KRFieldWrapperPkgUid, iStateMachine.WrapperPackageUid().iUid ); + + if (err != KErrNone) + { +failed: + SLOG3( "Resume info creation failed (%d: %d)", pos, err ); + // Remove the resume info file in error situation as it might not contain + // all the information needed to continue the installation + const TInt deleteStatus = iStateMachine.FsSession().Delete(KADMResumeInfoFile); + SLOG2( "Resume info has been removed (%d)", deleteStatus ); + } + + if (pos) + { + outFile.Close(); + } + + SLOG2( "- CreateInstallationResumeInfo(): %d", err ); + + return err; + } + +// ----------------------------------------------------------------------------- +// BackupFiles +// In case of failure during the complete installation process, +// backup the required files for future launch. +// ----------------------------------------------------------------------------- +// +void TStatePrepareExitWithError::BackupFilesL() const + { +#if 0 + HBufC* destFileName = NULL; + TInt err = KErrNone, pos = 0; + + // Backup the main application sis file to a folder. - Copy to ADM Private folder?! + TParsePtrC appFileName( *iStateMachine.SisFilename() ); + + destFileName = HBufC::NewLC( appFileName.NameAndExt().Length() + KADMPrivatePath().Length() ); + TPtr nameBufPtr = destFileName->Des(); + + nameBufPtr.Append( KADMPrivatePath ); + nameBufPtr.Append( appFileName.NameAndExt() ); + + // TODO: Do the file copying in AO + CFileMan* fileMan = CFileMan::NewL( iStateMachine.FsSession() ); + CleanupStack::PushL(fileMan); + err = fileMan->Copy( *iStateMachine.SisFilename(), *destFileName ); + CleanupStack::PopAndDestroy(fileMan); + + if (err != KErrNone) + { +failed: + CleanupStack::PopAndDestroy(destFileName); + SLOG3( "Failed to create backup (%d: %d)", pos, err ); + User::Leave(err); + } + + // Backup the info. The file is not present yet. + RFile outFile; + err = outFile.Replace( iStateMachine.FsSession(), KADMResumeInfoFile, EFileWrite | EFileStreamText | EFileShareAny ); + CHECK(err); + + // TODO: Backup the depfile in case of Bootstrap version 2 and save the name as well. + + // Write the AppFilename to resume info file. + err = WriteToConfigFile( outFile, KAppFileName, destFileName->Des() ); + CHECK(err); + CleanupStack::PopAndDestroy(); // destFileName +#endif + // Backup the info. The file is not present yet. + RFile outFile; + + // If no resume info file exists, create a new one. + // In case a resume info file exists, replace the file with a new one. + // (One resume information file supported at any point of time) + TInt err = outFile.Replace( iStateMachine.FsSession(), KADMResumeInfoFile, EFileWrite | EFileStreamText | EFileShareAny ); + + if (err != KErrNone) + { + goto failed; + } + + // TODO: Backup the depfile in case of Bootstrap version 2 and save the name as well. + + // Write the AppFilename to resume info file. + err = WriteToConfigFile( outFile, KRFieldAppFileName, iStateMachine.SisFilename()->Des() ); + + if (err != KErrNone) + { +failed: + SLOG2( "- BackupFiles(): LEAVE %d", err ); + outFile.Close(); + User::Leave(err); + } + + outFile.Close(); + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateInstallDependency::TStateInstallDependency(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateInstallDependency::Enter() const + { + SLOG3( "Installing '%S' to %c:", iStateMachine.iDownloadedFile, (char)iStateMachine.iCurrentPackage->GetInstallDrive() ); + // Replace the downloading note with installing note + iStateMachine.ShowInstallingWaitNoteL( iStateMachine.iCounterCurrent, iStateMachine.iCounterMax ); + iStateMachine.StopProgressBar(); + + // TODO: FIX THIS! This is an ugly hack to give time for S60 DlMgr AOs to + // finish their cleanups. To fix this properly, we need to wait till we get + // "EHttpDlDeleting" event before starting the installation. This has to be + // called before CancelAllDownloads(), otherwise DlMgrServer will die in + // CBase91 PANIC. + User::After(1000000); + // We need to remove the download(s) before starting the installation. + // Otherwise we might get KErrInUse error while installing. This happens + // quite often when using E: / F: drive as the download drive. + iStateMachine.iDownloadHandler->CancelAllDownloads(); + + // Wait for the "DlDeleting" event + iStateMachine.iDlDeletingSemaphore.Wait(KDlDeletingTimeout); + + iStateMachine.iInstallManager->SilentInstallPackageL( + *iStateMachine.iDownloadedFile, + iStateMachine.iCurrentPackage->GetInstallDrive() + ); + + iStateMachine.UpdateProgressBar(iStateMachine.iCumulativeDepDownloaded, + iStateMachine.iProgressMaxValue ); + } + +void TStateInstallDependency::Exit() const + { + // Remove the wait note +#if 0 + if ( !iStateMachine.OviStoreRunning() ) + { + iStateMachine.StopWaitNote(); + } +#endif + } + +void TStateInstallDependency::InstallCompleted(TInt aStatus) + { + LOG_FUNC + + CPackageInfo *curr = iStateMachine.iDepTree->GetCurrentFetchNode(); + User::LeaveIfNull(curr); + SLOG3( "Removing '%S%S'", &iStateMachine.iDepTree->GetDownloadPath(), curr->GetSisPackageName() ); + + // Delete the .sis file as its no longer required. + iStateMachine.iDepTree->DeleteFile( iStateMachine.FsSession(), + *curr->GetSisPackageName(), + iStateMachine.iDepTree->GetDownloadPath() + ); + if (aStatus == KErrNone) + { + if (curr->GetPackageStatus() == EPackageToBeFetched ) + { + curr->SetPackageStatus(EPackageFetchedInstalled); + } + else if (curr->GetPackageStatus() == EPackageRootToBeInstalled ) + { + curr->SetPackageStatus(EPackageRootInstalled); + } + else + { + SLOG2( "Unexpected package status %d", curr->GetPackageStatus() ); + } + + // Dependency package succesfully installed, update the counter to keep UI happy + iStateMachine.iCounterCurrent++; + // Download the next dependency + iStateMachine.SetState( CStateFactory::EStateDownloadDependency ); + } + else + { + SLOG2( "Dependency installation failed %d", aStatus ); + iStateMachine.SetFailureReason( EInstallationFailed ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + } + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +TStateInstallAppSis::TStateInstallAppSis(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateInstallAppSis::Enter() const + { + // We need to find the root package + if (iStateMachine.iCurrentPackage == NULL) + { + SLOG( "Finding root package" ); + CPackageInfo* package = NULL; + while ( (package = iStateMachine.iDepTree->GetNextFetchNode()) != NULL ) + { + if (package->GetPackageStatus() == EPackageRootToBeInstalled) + { + SLOG3( "Root: UID 0x%08X, '%S'", package->GetPackageUid(), package->GetPackageName() ); + break; + } + } + iStateMachine.iCurrentPackage = package; + } + // iStateMachine.iCurrentPackage should now point to a correct packege, + // the application.sis, in this case. Just make sure, it really does that. + // Do we have a package set? + if ( iStateMachine.iCurrentPackage ) + { + if ( iStateMachine.BootstrapVersion() == 3 ) + { +/* + SLOG( "Uninstalling smartinstaller (bootstrap)" ); + TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( + iStateMachine.WrapperPackageUid() ); + SLOG2( "Uninstall status: %d", err); +*/ + // Delete the dependency file that was provided with the wrapper + // TODO: Remove if(), DeleteFile() copes with NULL filenames. + if ( iStateMachine.DepFilename() ) + { + iStateMachine.iAppUi.DeleteFile( *iStateMachine.DepFilename() ); + } + +#ifdef FEATURE_LAUNCH_INSTALLED_APP + SLOG( "Dependencies and application are installed, ready to launch!" ); + iStateMachine.SetState( CStateFactory::EStateLaunchApp ); +#else + SLOG( "Dependencies and application are installed, ready to exit!" ); + iStateMachine.SetState( CStateFactory::EStateExit ); +#endif + } + else + { + SLOG3( "* Installing AppSis 0x%08x '%S'", iStateMachine.iCurrentPackage->GetPackageUid(), iStateMachine.SisFilename() ); + + // TODO: Leave or not to leave? + //User::LeaveIfNull( iStateMachine.SisFilename() ); + + // Update the progress bar + iStateMachine.UpdateProgressBar( iStateMachine.iProgressMaxValue, + iStateMachine.iProgressMaxValue ); + + //First try silent install - if package is self-signed, this will fail + //according to policy, so retry with a normal install. + + //User might not have been prompted for drive selection. + //In that case, it is ok to prompt for drive selection as part of the Wrapper Package Installation. + //This will be the case when files are injected into the Bootstrap import folder and not in a common "?:/ADM" drive. + + TChar installDrive = iStateMachine.SisFilename()->Des()[0]; + installDrive.UpperCase(); + if (installDrive < 'A' || installDrive > 'Z') + { + installDrive = KNoDriveSelected; + } + + // If the wrapper installed drive is not selected, use the application.sis + // destination drive as the installed drive. This way the developer can + // control, if he/she wants to prompt the installation drive or force it + // already in the wrapper/installer .pkg file. This can be controlled + // by using '!' as the destination drive (=prompt) or by using a fixed + // drive letter A->Z (=don't prompt). + if ( iStateMachine.iWrapperInstalledDrive == KNoDriveSelected ) + { + iStateMachine.iWrapperInstalledDrive = installDrive; + } + + if ( iStateMachine.iWrapperInstalledDrive != KNoDriveSelected ) + { + SLOG3( "Installing AppSis '%S' to %c:", iStateMachine.SisFilename(), (char)iStateMachine.iWrapperInstalledDrive ); + + // First show the wait note and then remove progress bar. + // MUCH less flicker this way. + iStateMachine.ShowWaitNoteL( R_ADM_FINALIZING_INSTALLATION_WAIT_TEXT ); + iStateMachine.StopProgressBar(); + iStateMachine.iInstallManager->SilentInstallPackageL( *iStateMachine.SisFilename(), iStateMachine.iWrapperInstalledDrive ); + } + else + { + SLOG( "! No installation drive selected, prompting" ); + // User will be prompted for the drive. + + //TODO: This is a hack. Change the Installcompleted logic to accomodate this. + iStateMachine.StopWaitNote(); + iStateMachine.StopProgressBar(); + TInt aStatus = iStateMachine.iInstallManager-> + InstallPackage( *iStateMachine.SisFilename() ); + iStateMachine.SetNonSilentInstallation(ETrue); + iStateMachine.HandleInstallCompletedL( aStatus ); + } + } // if + } + else + { + // Hmm. We should've had a root package, but apparently we didn't. Odd. + SLOG( "! Unexpected root package. PANIC" ); + PanicInState(EPanicStateUnexpectedRootPackage); + } + } + +void TStateInstallAppSis::InstallCompleted(TInt aStatus) + { + LOG_FUNC + + //It is possible for silent installation of the main app to fail due to insufficient memory + //TODO: would it be wise to move the below conditions to InstallManager and have some different error returned to statemachine? + + if ( ( aStatus == SwiUI::KSWInstErrSecurityFailure ) || ( aStatus == SwiUI::KSWInstErrInsufficientMemory ) ) + { + // We have a wait note displayed. If silent install failed, + // it needs to be removed so the installer UI won't be affected. + iStateMachine.StopWaitNote(); + + //SwiUI collapses the real error, Swi::ESignatureSelfSigned is what we should + //be checking here. SwiUI::GetLastErrorL is not implemented + //If the silent install fails, because of a self signed package, try again + //with interactive install. + // This is a synchronous call which returns the control here after + // installation is finished (or cancelled, or an error occurs or..) + aStatus = iStateMachine.iInstallManager->InstallPackage( + *iStateMachine.SisFilename() ); + iStateMachine.SetNonSilentInstallation(ETrue); + } + else if ( aStatus == KErrNone ) + { + // We end up here, if the silent installation completes succesfully + iStateMachine.SetSilentInstallationOk(ETrue); + } + // Was installation succesful? + if (aStatus == KErrNone) + { + CPackageInfo *curr = iStateMachine.iDepTree->GetCurrentFetchNode(); + User::LeaveIfNull(curr); + //TODO: What if root package is set in HandleInstallComplete!!! + // The next if should _always_ be true here + if ( curr->GetPackageStatus() == EPackageRootToBeInstalled ) + { + curr->SetPackageStatus(EPackageRootInstalled); + } + else if ( curr->GetPackageStatus() != EPackageRootInstalled ) + { + SLOG2( "! Invalid root package status %d", curr->GetPackageStatus() ); + } + + CPackageInfo* fetchPackage = iStateMachine.iDepTree->GetNextFetchNode(); + + // TODO: This is duplicated code from TStateDownloadDependency + if ( !fetchPackage ) + { +/* + //end of list - we're finished + SLOG( "Uninstalling wrapper" ); + TInt err = iStateMachine.iInstallManager->SilentUnInstallPackage( + iStateMachine.WrapperPackageUid() ); + SLOG2( "Uninstall status: %d", err); + // Wrapper has been uninstalled, ready to exit +*/ +#ifdef FEATURE_LAUNCH_INSTALLED_APP + iStateMachine.SetState( CStateFactory::EStateLaunchApp ); +#else + iStateMachine.SetState( CStateFactory::EStateExit ); +#endif + return; + } + else + { + // We should be at the end of the list, but seems that we're not. + SLOG2( "! Unexpected package 0x%08X. PANIC", fetchPackage->GetPackageUid() ); + PanicInState(EPanicStateUnexpectedPackage); + } + } + else + { + const TExitReason reason = (aStatus == SwiUI::KSWInstErrUserCancel) + ? EUserCancelled + : EInstallationFailed; + iStateMachine.SetFailureReason( reason ); + iStateMachine.SetState( CStateFactory::EStatePrepareExitWithError ); + } + } + +// ----------------------------------------------------------------------------- +// Confirms the user to continue the download when download threshold is exceeded +// ----------------------------------------------------------------------------- +// +TStateConfirmDownload::TStateConfirmDownload(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateConfirmDownload::Enter() const + { + // Show download query as we are roaming + iStateMachine.ShowDownloadQueryL( R_ADM_DL_PROMPT_TEXT ); + // the next state will get invoked by the user reply to the query + } + +void TStateConfirmDownload::UserAccepted() + { + // This is called when the user has selected "Yes" in the confirmation query + iStateMachine.SetState( CStateFactory::EStateStartDependencyDownload ); + } + +// ----------------------------------------------------------------------------- +// Confirms the user to continue the download if device is roaming +// ----------------------------------------------------------------------------- +// +TStateConfirmRoamingDownload::TStateConfirmRoamingDownload(const CStateFactory& aFactory) : + TState(aFactory) + { + } + +void TStateConfirmRoamingDownload::Enter() const + { + // Show download query as we are roaming + iStateMachine.ShowDownloadQueryL( R_ADM_DL_PROMPT_TEXT, R_ADM_ROAMING_TEXT ); + // the next state will get invoked by the user reply to the query + } + +void TStateConfirmRoamingDownload::UserAccepted() + { + // This is called when the user has selected "Yes" in the confirmation query + iStateMachine.SetState( CStateFactory::EStateStartDependencyDownload ); + }