/*
* Copyright (c) 2009 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:
* Implementation of EmailShutter
*
*/
// Include Files
#include <e32base.h>
#include <e32std.h>
#include <e32des16.h> // Descriptors
#include <e32property.h> // RProperty
#include <apgtask.h> // TApaTaskList
#include <w32std.h> // RWsSession
#include <s32mem.h> // RDesRead/WriteStream
#include <AlwaysOnlineManagerClient.h> // RAlwaysOnlineClientSession
#include <CPsRequestHandler.h> // CPSRequestHandler
#include <centralrepository.h> // CRepository
#include <aisystemuids.hrh> // HomeScreen UIDs
#include "FreestyleEmailUiConstants.h" // FS Email UI UID
#include "fsmtmsconstants.h" // MCE, Phonebook & Calendar UIDs
#include "emailstoreuids.hrh" // KUidMessageStoreExe
#include "emailtrace.h"
#include "emailshutdownconst.h"
#include "emailshutter.h"
#include "emailservermonitorconst.h"
#include "emailservermonitorutilities.h"
#include "emailservermonitor.h"
// UI Applications to be closed
const TUid KApplicationsToClose[] =
{
KFSEmailUiUid, // Freestyle Email UI
{ SettingWizardUidAsTInt }, // TP Wizard
{ KMceAppUid }, // MCE
{ KPhoneBookUid }, // Phonebook 1 & 2
{ KCalendarAppUid1 } // Calendar
};
// Applications that should not be closed. Should include only system
// applications that free the email resources by some other means.
const TUid KApplicationsNotToBeClosed[] =
{
{ AI_SID_AIFW_EXE }, // HomeScreen
{ AI_UID3_AIFW_COMMON }, // HomeScreen
};
// Non-UI clients that need to be closed
const TUid KOtherClientsToClose[] =
{
{ KPcsServerProcessUidAsTInt }, // PCS server
{ FSMailServerUidAsTInt }, // FSMailServer
};
// Plugin processes that need to be closed
const TUid KPluginProcessesToClose[] =
{
// MfE plugin
{ 0x20012BEE }, // KUidEasStartup
{ 0x20012BEC }, // KUidEasTarmAccess
{ 0x20012BD4 }, // KEasLogSenderServer
{ 0x20012BE6 }, // KUidEasServer
// Oz plugin
{ 0x2002136A }, // monitor
{ 0x20021367 }, // server
};
// Message store processes that need to be closed
const TUid KMsgStoreProcessesToClose[] =
{
{ KUidMessageStoreExe }, // MessageStoreExe.exe
{ KUidEmailStorePreInstallExe } // MessageStorePreInstallExe
};
const TInt KEmailUidExtraBuffer = 2 * KEmailPlatformApiUidItemSize;
// ======== MEMBER FUNCTION DEFINITIONS ========
// ---------------------------------------------------------------------------
// Two-phased class constructor.
// ---------------------------------------------------------------------------
//
CEmailShutter* CEmailShutter::NewL()
{
FUNC_LOG;
CEmailShutter* self = CEmailShutter::NewLC();
CleanupStack::Pop( self );
return self;
}
// ---------------------------------------------------------------------------
// Two-phased class constructor, leaves newly created object in cleanup stack.
// ---------------------------------------------------------------------------
//
CEmailShutter* CEmailShutter::NewLC()
{
FUNC_LOG;
CEmailShutter* self = new (ELeave) CEmailShutter();
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
// ---------------------------------------------------------------------------
// Default constructor
// ---------------------------------------------------------------------------
//
CEmailShutter::CEmailShutter()
: CActive( EPriorityStandard ), iMonitor( NULL )
{
FUNC_LOG;
}
// ---------------------------------------------------------------------------
// Default destructor
// ---------------------------------------------------------------------------
//
CEmailShutter::~CEmailShutter()
{
FUNC_LOG;
iInstStatusProperty.Close();
iPlatformApiAppsToClose.Close();
}
// ---------------------------------------------------------------------------
// Second phase class constructor.
// ---------------------------------------------------------------------------
//
void CEmailShutter::ConstructL()
{
FUNC_LOG;
// Define and attach/subscribe to P&S to get the shutdown event from
// installation initiator(s)
TInt error = iInstStatusProperty.Define( EEmailPsKeyInstallationStatus,
RProperty::EInt,
KAllowAllPolicy,
KPowerMgmtPolicy );
if( error != KErrNone && error != KErrAlreadyExists )
{
ERROR( error, "RProperty::Define (EEmailPsKeyInstallationStatus) failed!" );
ERROR_1( error, " error code: ", error );
User::Leave( error );
}
error = iInstStatusProperty.Attach( KEmailShutdownPsCategory,
EEmailPsKeyInstallationStatus );
if( error != KErrNone )
{
ERROR( error, "RProperty::Attach (EEmailPsKeyInstallationStatus) failed!" );
ERROR_1( error, " error code: ", error );
User::Leave( error );
}
// Define P&S key used to register platform API applications
error = RProperty::Define( EEmailPsKeyPlatformApiAppsToCloseLength,
RProperty::EInt,
KAllowAllPolicy,
KWriteDeviceDataPolicy );
if( error != KErrNone && error != KErrAlreadyExists )
{
ERROR( error, "RProperty::Define (EEmailPsKeyPlatformApiAppsToCloseLength) failed!" );
ERROR_1( error, " error code: ", error );
}
// Define P&S key used to register platform API applications
error = RProperty::Define( EEmailPsKeyPlatformApiAppsToClose,
RProperty::EByteArray,
KAllowAllPolicy,
KWriteDeviceDataPolicy );
if( error != KErrNone && error != KErrAlreadyExists )
{
ERROR( error, "RProperty::Define (EEmailPsKeyPlatformApiAppsToClose) failed!" );
ERROR_1( error, " error code: ", error );
}
CActiveScheduler::Add(this);
}
// ---------------------------------------------------------------------------
// Starts observing P&S shutdown event
// ---------------------------------------------------------------------------
//
void CEmailShutter::StartObservingShutdownEvent()
{
FUNC_LOG;
SetActive();
iInstStatusProperty.Subscribe( iStatus );
}
// ---------------------------------------------------------------------------
// Set P&S key after installation is done
// ---------------------------------------------------------------------------
//
void CEmailShutter::SetPsKeyInstallationFinished()
{
FUNC_LOG;
TInt error = iInstStatusProperty.Set( EEmailPsValueInstallationFinished );
ERROR_1( error, "RProperty::Set error code: %d", error );
}
// ---------------------------------------------------------------------------
// Restart needed services after installation is finished
// ---------------------------------------------------------------------------
//
void CEmailShutter::RestartServicesAfterInstallation()
{
TRAP_IGNORE( StartAoPluginL() );
TRAP_IGNORE( StartPcsServerL() );
}
// ---------------------------------------------------------------------------
// Set pointer to Email Server Monitor object
// ---------------------------------------------------------------------------
//
void CEmailShutter::SetMonitor( CEmailServerMonitor* aMonitor )
{
iMonitor = aMonitor;
}
// ---------------------------------------------------------------------------
// RunL of active object
// ---------------------------------------------------------------------------
//
void CEmailShutter::RunL()
{
FUNC_LOG;
if( iStatus == KErrNone )
{
TInt value( 0 );
TInt error = iInstStatusProperty.Get( value );
ERROR_1( error, "RProperty::Get error code: %d", error );
// Verify that the P&S value indicates that installation is starting
if( error == KErrNone &&
value == EEmailPsValueInstallationStarting )
{
StartShutdown();
// Send "installation OK to continue" event to installation
// initiator once everyting is shutdown
error = iInstStatusProperty.Set( EEmailPsValueInstallationOkToStart );
ERROR_1( error, "RProperty::Set error code: %d", error );
// Finally shutdown ourselves also
CActiveScheduler::Stop();
}
else
{
// Re-subscribe
StartObservingShutdownEvent();
}
}
else
{
ERROR_1( iStatus.Int(), "CEmailShutter::RunL error code: %d", iStatus.Int() );
}
}
// ---------------------------------------------------------------------------
// Cancel active object
// ---------------------------------------------------------------------------
//
void CEmailShutter::DoCancel()
{
FUNC_LOG;
iInstStatusProperty.Cancel();
}
// ---------------------------------------------------------------------------
// End client applications gracefully
// ---------------------------------------------------------------------------
//
void CEmailShutter::EndClients()
{
FUNC_LOG;
// End UI applications, ignore errors
TRAP_IGNORE( EndApplicationsL() );
// Define and publish the P&S key to give the clients change to close
// themselves gracefully
PublishPsKey( EEmailPsKeyShutdownClients );
}
// ---------------------------------------------------------------------------
// End UI applications gracefully
// ---------------------------------------------------------------------------
//
void CEmailShutter::EndApplicationsL()
{
FUNC_LOG;
RWsSession session;
User::LeaveIfError( session.Connect() );
CleanupClosePushL( session );
TApaTaskList taskList( session );
// First end our own applications that are defined in hard coded list
TInt count = sizeof(KApplicationsToClose) / sizeof(TUid);
for( TInt i = 0; i<count; i++ )
{
TApaTask task = taskList.FindApp( KApplicationsToClose[i] );
if ( task.Exists() )
{
INFO_1( "Closing UI app with UID: %d", KApplicationsToClose[i].iUid );
task.EndTask();
}
}
// Then end applications that are registered in P&S as platform API users
for( TInt i = 0; i<iPlatformApiAppsToClose.Count(); i++ )
{
TApaTask task = taskList.FindApp( iPlatformApiAppsToClose[i] );
if ( task.Exists() )
{
INFO_1( "Closing API UI app with UID: %d", iPlatformApiAppsToClose[i].iUid );
task.EndTask();
}
}
CleanupStack::PopAndDestroy( &session );
}
// ---------------------------------------------------------------------------
// Define and publish the P&S key to inform all related services about the
// installation, so that they can shutdown themselves gracefully
// ---------------------------------------------------------------------------
//
void CEmailShutter::PublishPsKey( TInt aKey )
{
FUNC_LOG;
TInt error = RProperty::Define( aKey,
RProperty::EInt,
KAllowAllPolicy,
KPowerMgmtPolicy);
ERROR_1( error, "RProperty::Define error code: %d", error );
RProperty psProperty;
error = psProperty.Attach( KEmailShutdownPsCategory,
aKey );
ERROR_1( error, "RProperty::Attach error code: %d", error );
if( error == KErrNone )
{
error = psProperty.Set( KEmailShutterPsValue );
ERROR_1( error, "RProperty::Set error code: %d", error );
}
psProperty.Close();
}
// -----------------------------------------------------------------------------
// Closes all non-email related plugins/services
// -----------------------------------------------------------------------------
//
void CEmailShutter::Close3rdPartyServices()
{
FUNC_LOG;
TRAP_IGNORE( ClosePcsServerL() );
TRAP_IGNORE( CloseAOPluginL() );
}
// -----------------------------------------------------------------------------
// Sends command to Always Online server to stop fs email plugin
// -----------------------------------------------------------------------------
//
void CEmailShutter::CloseAOPluginL()
{
FUNC_LOG;
RAlwaysOnlineClientSession aoSession;
CleanupClosePushL( aoSession );
TPckgBuf<TUid> id( KAlwaysOnlineEmailPluginUid );
aoSession.SendSinglePacketL( EServerAPIBaseCommandStop, id );
CleanupStack::PopAndDestroy( &aoSession );
}
// -----------------------------------------------------------------------------
// Sends command to Always Online server to start fs email plugin
// -----------------------------------------------------------------------------
//
void CEmailShutter::StartAoPluginL() const
{
FUNC_LOG;
RAlwaysOnlineClientSession aoSession;
CleanupClosePushL( aoSession );
TPckgBuf<TUid> id( KAlwaysOnlineEmailPluginUid );
aoSession.SendSinglePacketL( EServerAPIBaseCommandStart, id );
CleanupStack::PopAndDestroy( &aoSession );
}
// -----------------------------------------------------------------------------
// Closes PCS (Predictive Contact Search) server
// -----------------------------------------------------------------------------
//
void CEmailShutter::ClosePcsServerL()
{
FUNC_LOG;
// If PCS server not running, don't request shutdown, because in this case
// the server need to be started before it can process the shutdown request
if( IsProcessRunning( KPcsServerProcessUidAsTInt ) )
{
INFO( "Closing PCS server" );
CPSRequestHandler* pcs = CPSRequestHandler::NewLC();
pcs->ShutdownServerL();
CleanupStack::PopAndDestroy( pcs );
}
}
// -----------------------------------------------------------------------------
// Starts PCS (Predictive Contact Search) server
// -----------------------------------------------------------------------------
//
void CEmailShutter::StartPcsServerL() const
{
FUNC_LOG;
CPSRequestHandler* pcs = CPSRequestHandler::NewL();
delete pcs;
}
// ---------------------------------------------------------------------------
// Try to find the UID given as parameter from the array given as parameter.
//
// @param aSid Process UID to be searched
// @param aArray Array from where to search
// @param aArrayCount Item count of the aArray
// @return ETrue if the UID can be found from the array, otherwise EFalse.
// ---------------------------------------------------------------------------
//
TBool CEmailShutter::FindFromArray(
const TUid aSid,
const TUid aArray[],
const TInt aArrayCount )
{
for( TInt i = 0; i < aArrayCount; i++ )
{
if( aArray[i] == aSid )
{
return ETrue;
}
}
return EFalse;
}
// ---------------------------------------------------------------------------
// Checks does this UID belong to the list of the services that we need to
// close down.
//
// @param aSid Process UID to check
// @param aMode Killing mode, are we now killing clients, plugins or msg store
// @return ETrue if this is one of the services we need to close in specified
// mode, otherwise EFalse
// ---------------------------------------------------------------------------
//
TBool CEmailShutter::NeedToKillThisProcess(
const TUid aSid,
const TEmailShutterKillingMode aMode )
{
if( aMode == EKillingModeClients ||
aMode == EKillingModeAll )
{
// In case of clients we need to check applications and other clients
TInt count = sizeof(KApplicationsToClose) / sizeof(TUid);
if( FindFromArray( aSid, KApplicationsToClose, count ) )
{
return ETrue;
}
count = sizeof(KOtherClientsToClose) / sizeof(TUid);
if( FindFromArray( aSid, KOtherClientsToClose, count ) )
{
return ETrue;
}
// Check also clients registered as platform API users
if( iPlatformApiAppsToClose.Find( aSid ) != KErrNotFound )
{
return ETrue;
}
}
if( aMode == EKillingModePlugins ||
aMode == EKillingModeAll )
{
TInt count = sizeof(KPluginProcessesToClose) / sizeof(TUid);
if( FindFromArray( aSid, KPluginProcessesToClose, count ) )
{
return ETrue;
}
}
if( aMode == EKillingModeMsgStore ||
aMode == EKillingModeAll )
{
TInt count = sizeof(KMsgStoreProcessesToClose) / sizeof(TUid);
if( FindFromArray( aSid, KMsgStoreProcessesToClose, count ) )
{
return ETrue;
}
}
return EFalse;
}
// ---------------------------------------------------------------------------
// Kills all the related processes gracelessly. This is used as a backup for
// those processes that didn't close themselves gracefully.
// ---------------------------------------------------------------------------
//
TBool CEmailShutter::KillEmAll(
const TEmailShutterKillingMode aMode, /* = EKillingModeAll*/
const TBool aOnlyCheckIfRunning /* = EFalse */ )
{
FUNC_LOG;
// Having tried graceful shutdown, we need to kill any remaining processes
// matching the SID. Note that killing a process may re-order the list of
// remaining processes, so the search must start from the top again.
TBool found( EFalse );
TBool needToScanFullList;
TFullName fullName;
do
{
needToScanFullList = EFalse;
TFindProcess findProcess;
while( findProcess.Next(fullName) == KErrNone )
{
RProcess process;
TInt error = process.Open( findProcess );
// In case of error just skip this process, don't leave
if( error == KErrNone )
{
TUid sid( process.SecureId() );
if ( NeedToKillThisProcess( sid, aMode ) && // Is this our process and
process.ExitType() == EExitPending ) // and is the process alive
{
found = ETrue;
// Kill the found process if aOnlyCheckIfRunning flag not set
if( !aOnlyCheckIfRunning )
{
INFO_1( "Killing process with UID: %d", sid.iUid );
process.Kill(KErrNone);
needToScanFullList = ETrue;
}
}
}
process.Close();
}
} while (needToScanFullList);
return found;
}
// ---------------------------------------------------------------------------
// Highest level function to close everything in right order
// ---------------------------------------------------------------------------
//
void CEmailShutter::StartShutdown()
{
FUNC_LOG;
// Cancel Email Server monitoring before closing the server
if( iMonitor )
{
iMonitor->Cancel();
}
// First read the platform API UIDs from P&S, those are needed later
TRAP_IGNORE( ReadPlatformApiUidsL() );
// End all clients
EndClients();
// Wait some time to give the clients some time to shut down themselves
Wait( KTimeToWaitApplicationsInSeconds );
// End all 3rd party clients
Close3rdPartyServices();
WaitInCycles( KMaxTimeToWaitClientsInSeconds, EKillingModeClients );
// Kill gracelessly all remaining clients
KillEmAll( EKillingModeClients );
// Define and publish the P&S key to give the plugin servers change to
// close themselves gracefully
PublishPsKey( EEmailPsKeyShutdownPlugins );
// Wait some time to give the plugins some time to shut down themselves
WaitInCycles( KMaxTimeToWaitPluginsInSeconds, EKillingModePlugins );
// Kill gracelessly all remaining plugin processes
KillEmAll( EKillingModePlugins );
// Define and publish the P&S key to give the msg store change to close
// itself gracefully
PublishPsKey( EEmailPsKeyShutdownMsgStore );
// Wait some time to give the msg store some time to shut down itself
WaitInCycles( KMaxTimeToWaitMsgStoreInSeconds, EKillingModeMsgStore );
// Kill gracelessly all remaining message store processes
KillEmAll( EKillingModeMsgStore );
// Kill gracelessly all remaining processes
KillEmAll();
INFO( "Finished Shutdown" );
}
// ---------------------------------------------------------------------------
// Waits in cycles and checks between cycles have all relevant processes
// closed or do we still need to wait. Returns when either aMaxWaitTime
// has elapsed or when all relevant processes have been shutdown.
// ---------------------------------------------------------------------------
//
void CEmailShutter::WaitInCycles(
const TInt aMaxWaitTime,
const TEmailShutterKillingMode aMode )
{
FUNC_LOG;
TInt totalWaitTime = 0;
do
{
// Do the wait and increase total waiting time counter
Wait( KWaitTimeCycleInSeconds );
totalWaitTime += KWaitTimeCycleInSeconds;
// Do this as long as aMaxWaitTime has not elapsed and
// there are some process(es) to wait for
} while ( ( totalWaitTime < aMaxWaitTime ) && KillEmAll( aMode, ETrue ) );
}
// ---------------------------------------------------------------------------
// Reads platform API process UIDs from Publish and Subscribe key
// ---------------------------------------------------------------------------
//
void CEmailShutter::ReadPlatformApiUidsL()
{
FUNC_LOG;
iPlatformApiAppsToClose.Reset();
// Read buffer length
TInt bufLength( 0 );
User::LeaveIfError( RProperty::Get( KEmailShutdownPsCategory,
EEmailPsKeyPlatformApiAppsToCloseLength,
bufLength ) );
// Allocate buffer for reading and then read the list of UIDs from P&S.
// Adding some extra buffer just in case the size key and actual list
// are out of sync. This shouldn't happen, but you never know.
HBufC8* readBuf = HBufC8::NewLC( bufLength + KEmailUidExtraBuffer );
TPtr8 readPtr = readBuf->Des();
User::LeaveIfError( RProperty::Get( KEmailShutdownPsCategory,
EEmailPsKeyPlatformApiAppsToClose,
readPtr ) );
RDesReadStream readStream( readPtr );
CleanupClosePushL( readStream );
// Get items count from the actual buffer
TInt itemsCount = readPtr.Length() / KEmailPlatformApiUidItemSize;
for ( TInt ii = 0;ii < itemsCount; ++ii )
{
// Read next UID from the stream
TUid item = TUid::Uid( readStream.ReadInt32L() );
// Append only UIDs of such applications that should be closed
TInt count = sizeof(KApplicationsNotToBeClosed) / sizeof(TUid);
if( !FindFromArray( item, KApplicationsNotToBeClosed, count ) )
{
iPlatformApiAppsToClose.AppendL( item );
}
}
CleanupStack::PopAndDestroy( 2, readBuf );
}