/*
* Copyright (c) 2004-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:
*
*/
#include "statemachine.h"
#include "log.h"
#include "plan.h"
#include "swispubsubdefs.h"
namespace Swi
{
//
// CSwisStateMachine
//
CSwisStateMachine::CSwisStateMachine(const RMessage2& aMessage)
: CActive(EPriorityNormal),
iMessage(aMessage)
{
CActiveScheduler::Add(this);
}
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
CSwisStateMachine::CSwisStateMachine(const RMessage2& aMessage,TBool aInInfoMode)
: CActive(EPriorityNormal),
iMessage(aMessage)
{
CActiveScheduler::Add(this);
// Set the state machine to operate on component information extraction mode
// (simply extract and return the info don't install the component).
iIsInInfoMode = aInInfoMode;
}
#endif
CSwisStateMachine::~CSwisStateMachine()
{
Cancel();
// close UISS session
iUiHandler.Close();
delete iProgressPublisher;
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
iStsSession.Close();
delete iRegistryWrapper;
#else
delete iIntegrityServices;
#endif
// close SWI Observer session
iObserver.Close();
}
void CSwisStateMachine::ConstructL()
{
// mark the installation/un-installation operation as unconfirmed to start with
iOperationConfirmed = EFalse;
iProgressPublisher = Swi::CProgressBarValuePublisher::NewL();
iUiHandler.SetProgressBarValuePublisher(iProgressPublisher);
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
iStsSession.CreateTransactionL();
iRegistryWrapper = CRegistryWrapper::NewL();
#else
// Create integrity services, use the current time as transaction ID
TTime currentTime;
currentTime.UniversalTime();
_LIT(KIntegrityServicesPath, "\\sys\\install\\integrityservices\\");
iIntegrityServices=CIntegrityServices::NewL(currentTime.Int64(),
KIntegrityServicesPath);
// attempt to recover any previously failed installations
// do not let failed rollback prevent this installation from taking place
TRAP_IGNORE(iIntegrityServices->RollBackL(ETrue));
if (iIntegrityServices->StartedJournalRollback())
{
// At least one journal file started being rolled back. In case any
// registry entries were removed, the cache must be regenerated.
ResetRegistryCache();
}
#endif
}
TInt CSwisStateMachine::Start()
{
// connect to the UISS
TInt err=iUiHandler.Connect();
if (err!=KErrNone)
{
return err;
}
// kick off
CompleteSelf();
SetActive();
return KErrNone;
}
void CSwisStateMachine::CompleteSelf()
{
TRequestStatus* status=&iStatus;
User::RequestComplete(status, KErrNone);
}
void CSwisStateMachine::ChangeStateL(TState* aNextState)
{
// Enter aNextState and make it our current state
TInt err=KErrNone;
while (aNextState)
{
// State change required
iState=aNextState;
TRAP(err, iState->EnterL());
aNextState=err ? ErrorOnStateEntryL(err) : NULL;
}
if (err!=KErrNone && aNextState==NULL)
{
// There was an error and we couldn't recover from it so leave with the error
User::Leave(err);
}
}
void CSwisStateMachine::RunL()
{
if (iCancelled)
{
FinalizeJournalsL(KErrCancel);
PostJournalFinalizationL(KErrCancel);
CompleteClientL(KErrCancel);
return;
}
if (iState==NULL)
{
ChangeStateL(FirstState());
}
else
{
// See how the last SetActive() ended up
TInt err=iStatus.Int();
// Decide what to do next
TState* nextState=NULL;
if (err==KErrNone)
{
// Everything was OK, leave this state
nextState=iState->CompleteL();
}
else
{
// Something went wrong
nextState=iState->ErrorL(err);
}
// Move on to the next state (could be NULL, in which case we're done)
ChangeStateL(nextState);
if (nextState==NULL)
{
FinalizeJournalsL(KErrNone);
PostJournalFinalizationL(KErrNone);
CompleteClientL(KErrNone);
}
}
}
void CSwisStateMachine::PostJournalFinalizationL(TInt /*aError*/ )
{
}
void CSwisStateMachine::CompleteClientL(TInt aError)
{
DEBUG_PRINTF2(_L8("Completing Install/Uninstall client with code '%d'"), aError);
iMessage.Complete(aError);
}
/**
* If there is no error signals the client that the (un)install has finished before
* commiting the journals and kicking off the observer.
* If there is an error the journals are rolled back.
*/
void CSwisStateMachine::FinalizeJournalsL(TInt aError)
{
if(aError == KErrNone)
{
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
// When the state machine runs in non info collection (ie,. installation / uninstallation) mode,
// We should signal the client before committing the transactions, as the client can still
// abort the operation at the callback.
if (!IsInInfoMode())
{
SignalCompletedL();
// NOTE: There is a caveat here as we need to commit two independant transactions and there
// exists a possibility that the second commit fails and this would leave the device in an
// inconsistent state - there is no known solution for this
// Due to the fact that SCR allows only one transaction at a time (one writer, many readers),
// the possibility of the registry commit (SCR commit) failing is less likely than an STS commit
// failure - therefore we commit STS first and then the registry
iStsSession.CommitL();
iRegistryWrapper->CommitMutableOperationsL();
//If the operation is successfull, call Commit of the SWI Observer.
//Otherwise, do not commit to allow SWI Observer to delete the observation file.
iObserver.CommitL();
}
else
{
// We are runing in information collection mode. So, simply ignore the transaction details.
// Close SWI Observer session to release the observation log file handle.
// Otherwise, the rollback would not be able to delete the swi observer log file.
iObserver.Close();
iStsSession.RollBackL();
iRegistryWrapper->RollbackMutableOperationsL();
}
#else
SignalCompletedL();
iIntegrityServices->CommitL();
//If the operation is successfull, call Commit of the SWI Observer.
//Otherwise, do not commit to allow SWI Observer to delete the observation file.
iObserver.CommitL();
#endif
}
else
{
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
// Close SWI Observer session to release the observation log file handle.
// Otherwise, the rollback would not be able to delete the swi observer log file.
iObserver.Close();
iStsSession.RollBackL();
iRegistryWrapper->RollbackMutableOperationsL();
#else
// Close SWI Observer session to release the observation log file handle.
// Otherwise, the rollback would not be able to delete the swi observer log file.
iObserver.Close();
iIntegrityServices->RollBackL(EFalse);
ResetRegistryCache();
#endif
}
}
// Inform the user of a fatal error.
void CSwisStateMachine::InformUserOfFatalErrorL(TInt aError)
{
// Try to inform the user
TAppInfo appInfo(KNullDesC, KNullDesC, TVersion());
switch (aError)
{
// We can get "file corrupt" error when
// getting the controller, at which point the planner is not yet
// available.
case KErrCorrupt: // file corrupt
{
CDisplayError* cmd=CDisplayError::NewLC(appInfo, EUiFileCorrupt,
KNullDesC);
iUiHandler.ExecuteL(*cmd);
CleanupStack::PopAndDestroy(cmd);
break;
}
// If the disk is not ready on reading controller or logo, etc.
// then handle this with a disk not present error note
case KErrNotReady:
{
CDisplayError* event=CDisplayError::NewLC(appInfo, EUiDiskNotPresent, KNullDesC);
iUiHandler.ExecuteL(*event);
CleanupStack::PopAndDestroy(event);
break;
}
case KErrDiskFull:
{
CDisplayError* event=CDisplayError::NewLC(appInfo, EUiInsufficientSpaceOnDrive, KNullDesC);
iUiHandler.ExecuteL(*event);
CleanupStack::PopAndDestroy(event);
break;
}
default:
{
break;
}
}
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
iStsSession.RollBackL();
iRegistryWrapper->RollbackMutableOperationsL();
#else
iIntegrityServices->RollBackL(EFalse);
#endif
}
#ifndef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
void CSwisStateMachine::ResetRegistryCache(void)
{
// Need to get the registry to reload its cache since we may have deleted
// files which were in it.
// Nothing we can do about errors during rollback here
RSisRegistryWritableSession session;
if (KErrNone==session.Connect())
{
TRAP_IGNORE(session.RegenerateCacheL());
session.Close();
}
}
#endif
// This is called if RunL() leaves.
TInt CSwisStateMachine::RunError(TInt aError)
{
DEBUG_PRINTF2(_L8("Install/Uninstall Statemachine RunL() leave with code '%d'"), aError);
TInt property = 0;
if (RProperty::Get(KUidSystemCategory, KUidSoftwareInstallKey, property) == KErrNone)
{
RProperty::Set(KUidSystemCategory, KUidSoftwareInstallKey, property | ESwisStatusAborted);
}
// Close SWI Observer session to release the observation log file handle.
// Otherwise, the rollback would not be able to delete the swi observer log file.
iObserver.Close();
// RunError cannot leave so ignore UI problems when attempting
// to display fatal error messages.
TRAPD(err, InformUserOfFatalErrorL(aError));
if(err != KErrNone)
{
// Dialog failed so use its error code instead?!
aError = err;
}
#ifndef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
// don't want to regenerate the sisregistry if the user has declined the install/un-install
// and the registry has not been altered, as this makes the UI unresponsive.
if (iOperationConfirmed)
{
// re-generate the sisregistry cache .. very time consuming!
ResetRegistryCache();
}
#endif
iMessage.Complete(aError);
return KErrNone;
}
void CSwisStateMachine::DoCancel()
{
}
// called when an install is cancelled whilst files are being copied
void CSwisStateMachine::CancelInstallation()
{
DEBUG_PRINTF(_L8("Install/Uninstall cancelled"));
if (!iCancelled)
{
iCancelled=ETrue;
if(iState)
{
// cancel install
iState->Cancel();
}
}
}
void CSwisStateMachine::SetNextState(TState* aState)
{
iState=aState;
}
void CSwisStateMachine::HandleInstallationEventL(CPlan* plan, TInstallEvent aEvent, TInt aValue)
{
TInt err=KErrGeneral;
if (plan)
{
TRAP(err, plan->AppInfoL());
}
if (err==KErrNone)
{
CHandleInstallEvent* cmd = CHandleInstallEvent::NewLC(plan->AppInfoL(), aEvent, aValue, KNullDesC);
UiHandler().ExecuteL(*cmd);
if (!cmd->ReturnResult())
{
User::Leave(KErrCancel);
}
CleanupStack::PopAndDestroy(cmd);
}
else
{
TAppInfo appInfo(KNullDesC, KNullDesC, TVersion());
CHandleInstallEvent* cmd = CHandleInstallEvent::NewLC(appInfo, aEvent, aValue, KNullDesC);
UiHandler().ExecuteL(*cmd);
if (!cmd->ReturnResult())
{
User::Leave(KErrCancel);
}
CleanupStack::PopAndDestroy(cmd);
}
}
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
TBool CSwisStateMachine::IsInInfoMode()
{
return iIsInInfoMode;
}
void CSwisStateMachine::SetIsInInfoMode(TBool aOperationalMode)
{
iIsInInfoMode = aOperationalMode;
}
#endif
void CSwisStateMachine::SetFinalProgressBarValue(TInt aValue)
{
iProgressPublisher->SetFinalProgressBarValue(aValue);
}
} // namespace Swi