--- /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 <ADM.rsg>
+#include "ADMApplication.h" // KUidADMApp
+#include "ADMStateMachine.h"
+#include "ADMStateMachineDebug.h"
+#include "globals.h"
+#include "config.h"
+
+#include <e32std.h>
+#include <sysutil.h> // SysUtil::DiskSpaceBelowCriticalLevel()
+#include <BAUTILS.H>
+#include <apgcli.h>
+
+// 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<TUid> 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<KMaxFileName> 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 );
+ }