smartinstaller/adm/src/ADMStateMachine.cpp
author Santosh V Patil <santosh.v.patil@nokia.com>
Wed, 30 Jun 2010 11:01:26 +0530
branchADM
changeset 48 364021cecc90
permissions -rw-r--r--
SmartInstaller contribution based on the Nokia Qt SDK 1.0 release

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