// Copyright (c) 1997-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:
//
#ifndef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "shutdownsrv.h"
#else //SYMBIAN_ENABLE_SPLIT_HEADERS
#include "shutdownsess.h"
#endif //SYMBIAN_ENABLE_SPLIT_HEADERS
#include "savenotf.h"
#include "savepriv.h"
#include "shutdowntimer.h"
#include <hal.h>
#include <f32file.h>
#include <bautils.h>
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
#include <s32mem.h>
#include <ssm/ssmpatchableconstants.h>
#endif
//The function used to start the profiler, while profiling the shutdown.
#ifdef __PROFILE_SHUTDOWN
_LIT(KProfilerCmd,"start -noui");
void StartProfiler()
{
RProcess p;
TInt r=p.Create(KProfilerName,KProfilerCmd);
if (r==KErrNone)
{
p.Resume();
p.Close();
}
}
#endif
//The function used to unload the profiler, while profiling the shutdown.
#ifdef __PROFILE_SHUTDOWN
void UnloadProfiler()
{
TFullName name;
TFindServer findSvr(KProfilerName);
Profiler::Unload();
while(findSvr.Next(name) == KErrNone)
{
User::After(1000000);
findSvr.Find(KProfilerName);
}
}
#endif
//
// class CServShutdownServer
//
/**
Standard phase-one factory method for creating CServShutdownServer instances.
@return A newly-constructed shutdown server object.
@leave Some system-wide error codes including KErrNoMemory.
*/
EXPORT_C CServShutdownServer* CServShutdownServer::NewL()
{ // static
CServShutdownServer* self=new(ELeave) CServShutdownServer(CActive::EPriorityStandard);
return self;
}
/**
Destructor.
Releases CShutdownTimer object if it has been allocated.
*/
EXPORT_C CServShutdownServer::~CServShutdownServer()
{
delete iShutdownTimer;
}
/**
This method iterates through all session instances and checks that all they have pending
requests (which means that all clients have completed their powerdown related processing
and re-registered itself back to the shutdown server).. If that's true (all sessions have
pending requests) and iPowerOff flag is nonzero, the method will call SwitchOff() - the
power will be switched off.
@see CServShutdownServer::SwitchOff()
@see CServShutdownSession::HasPendingRequest()
*/
EXPORT_C void CServShutdownServer::HandlePowerNotifRequest(const RThread& /*aClient*/)
{
// if all sessions have a pending request then they've all saved their data and we can switch off
TBool completed=ETrue;
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
if (!static_cast<CServShutdownSession*>(session)->HasPendingRequest())
{
completed=EFalse;
break;
}
}
if (completed && iPowerOff)
{
SwitchOff();
}
}
/**
This method will call SwitchOff(), if there are no sessions, so - no clients.
If there are registered clients (sessions), the method will call CServShutdownSession::NotifySave()
for each of them.
If the timer object has been initialised, it calculates the shutdown timeout value
and adds the timer to the active scheduler.
@param aSaveType The action, which will be given to the clients, when calling their
MSaveObserver::SaveL() implementations.
@see CServShutdownServer::SwitchOff()
@see CServShutdownSession::NotifySave()
*/
EXPORT_C void CServShutdownServer::NotifySave(MSaveObserver::TSaveType aSaveType)
{
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
CSession2* p=iter++;
if (p==NULL)
{
if (iPowerOff)
{
SwitchOff();
}
}
else
{
TInt numClients = 0;
iter.SetToFirst();
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
static_cast<CServShutdownSession*>(session)->NotifySave(aSaveType);
numClients++;
}
if(iShutdownTimer && !iShutdownTimer->IsActive())
{
iShutdownTimer->Start(numClients);
}
}
}
/**
@return Non-zero If the powerdown sequence has been initiated.
*/
EXPORT_C TBool CServShutdownServer::IsPowerOff() const
{
return iPowerOff;
}
/**
Cancels any power off request.
*/
EXPORT_C void CServShutdownServer::CancelPowerOff()
{
iPowerOff=EFalse;
// Cancel the timer.
if (iShutdownTimer)
{
iShutdownTimer->Cancel();
}
}
/**
@param aPriority The active object priority.
*/
EXPORT_C CServShutdownServer::CServShutdownServer(TInt aPriority):
CServer2(aPriority), iShutdownTimer(0)
{
}
/**
Completes the server construction by adding the server to the active scheduler and initializing the
CShutdownTimer object if applicable.
@leave See CServer2::StartL() leave error codes.
@see CServer2::StartL()
@panic KErrNotSupported Incorrect patchable variables configuration.
*/
EXPORT_C void CServShutdownServer::ConstructL()
{
iShutdownTimer = CShutdownTimer::NewL(*this);
#ifdef TEST_SHUTDOWN_SERVER
StartL(__TEST_SHUTDOWN_SERVER_NAME);
#else
StartL(__SHUTDOWN_SERVER_NAME);
#endif
}
/**
This method switches off the power using the Power API.
If there is a defined SYSLIBS_TEST macro, the method does nothing.
*/
void CServShutdownServer::DoSwitchOff()
{
//Finalize the drives before shutting down
RFs fs;
TInt r=fs.Connect();
if (r==KErrNone)
{
//Ignore the error code, as it is not a critical call
r=fs.FinaliseDrives();
fs.Close();
}
#ifdef SYSLIBS_TEST
// Test mode, only prints debug message.
RDebug::Printf("CServShutdownServer::SwitchOff() gets run in SYSLIBS_TEST mode.\n");
#else //SYSLIBS_TEST
// restart or standby/shutdown using Power API
if (Power::EnableWakeupEvents(iPowerEvent) == KErrNone)
{
// Prepare to wake up if power event is standby
TRequestStatus s;
Power::RequestWakeupEventNotification(s);
Power::PowerDown(); // if event is Restart, this function should never return
User::WaitForRequest(s);
}
#endif //SYSLIBS_TEST
}
void CServShutdownServer::SwitchOff()
{
// If the timer has applied, cancel any outstanding requests,
// no matter SwitchOff() has been triggered by the timer or the shutdown server.
if (iShutdownTimer)
{
iShutdownTimer->Cancel();
}
if (iPowerOff)
{
#ifdef __PROFILE_SHUTDOWN
UnloadProfiler();
#endif //__PROFILE_SHUTDOWN
}
//SSM shutdown is used only when patchable constant KSsmGracefulShutdown is true
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
if (iPowerOff && !IsSsmGracefulShutdown())
#else
if (iPowerOff)
#endif
{
//Shutdown using old shutdownsrv, not SSM
DoSwitchOff();
}
iPowerOff = EFalse;
}
/**
This method creates a new server side session object.
@param aVersion Shutdown server version number.
@return A pointer to the created session object.
@leave KErrNotSupported Unknown shutdown server version
@leave Some system-wide error codes including KErrNoMemory.
*/
CSession2* CServShutdownServer::NewSessionL(const TVersion& aVersion,const RMessage2& /*aMessage*/) const
{
TVersion v(KShutdownMajorVN,KShutdownMinorVN,KShutdownBuildVN);
if (!User::QueryVersionSupported(v,aVersion))
User::Leave(KErrNotSupported);
CSession2* pSession = CServShutdownSession::NewL();
return pSession;
}
/**
This method has to be called, when the registered clients have to be notified that a
particular action has to be done, such as MSaveObserver::ESaveData, MSaveObserver::ESaveAll,
MSaveObserver::EReleaseRAM,...
If this is a beginning of a powerdown sequence, the method will store the locales and the HAL
properties.
If the requested action is not MSaveObserver::ESaveNone, the method will call
CServShutdownServer::NotifySave().
@param aAction The type of the requested action
@param aPowerOff If it is non-zero, this is the beginning of a powerdown sequence.
@param aEvent The type of the powerdown event (power off or restart)
@leave KErrNotSupported Leaves if aEvent is invalid
@see CServShutdownServer::NotifySave()
@see TPowerState
*/
EXPORT_C void CServShutdownServer::HandleShutdownEventL(MSaveObserver::TSaveType aAction,TBool aPowerOff, TPowerState aEvent)
{
if( aPowerOff )
{
__ASSERT_ALWAYS((aEvent>EPwActive)&&(aEvent<EPwLimit), User::Leave(KErrNotSupported));
#ifdef __PROFILE_SHUTDOWN
StartProfiler();
#endif
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
if (!IsSsmGracefulShutdown())
{
// Save the state of the HAL
// The state of the locale should be saved before the HAL
BaflUtils::PersistLocale();
BaflUtils::PersistHAL();
}
#else
// Save the state of the HAL
// The state of the locale should be saved before the HAL
BaflUtils::PersistLocale();
BaflUtils::PersistHAL();
#endif
}
iPowerOff=aPowerOff;
iPowerEvent = aEvent;
// add LAF setting for powering down screen (& keyboard??)
if (aAction != MSaveObserver::ESaveNone)
{
NotifySave(aAction);
}
}
/**
This method returns an information about the shutdown status.
@param aPowerOff An output parameter, where iPowerOff value will be stored.
It will be non-zero, if a powerdown sequence has been initiated.
@param aAllSessionsHavePendingRequest An output parameter. It will be non-zero, if
all clients has pending requests.
*/
EXPORT_C void CServShutdownServer::GetShutdownState(TBool& aPowerOff, TBool& aAllSessionsHavePendingRequest) const
{
aAllSessionsHavePendingRequest = AllSessionsHavePendingRequest();
aPowerOff = iPowerOff;
}
/**
This method creates an array of CArrayFix<TThreadId> type and appends to it the
thread id-s of the all registered clients.
The created CArrayFix<TThreadId> instance will be pushed on the cleanup stack.
@return A pointer to a CArrayFix<TThreadId> array with the client thread id-s.
@leave Some system-wide error codes including KErrNoMemory.
*/
EXPORT_C CArrayFix<TThreadId>* CServShutdownServer::ClientArrayLC()
{
CArrayFix<TThreadId>* clientArray=new(ELeave) CArrayFixFlat<TThreadId>(2);
CleanupStack::PushL(clientArray);
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
TKeyArrayFix key(0,ECmpTInt);
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
TThreadId id=static_cast<CServShutdownSession*>(session)->ClientThreadId();
TInt pos;
if (clientArray->Find(id,key,pos)!=0)
{
clientArray->AppendL(id);
}
}
return clientArray;
}
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
/**
This method will write thread id-s of all clients that are registered for shutdown notification in to a streem.
@param aMessage consists of buffer, clientside array count and server side array count
*/
void CServShutdownServer::ClientArrayL(const RMessage2& aMessage)
{
const TInt arrayItemCount = ClientArrayCount();
//write all registered client array in a buffer only when client side
//array count is equal to server side array count
if(arrayItemCount == aMessage.Int1())
{
const TInt sizeRequired = arrayItemCount * sizeof(TThreadId);
CBufFlat* const buf=CBufFlat::NewL(sizeRequired);
CleanupStack::PushL(buf);
RBufWriteStream writeStream(*buf);
CleanupClosePushL(writeStream);
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
TThreadId id=static_cast<CServShutdownSession*>(session)->ClientThreadId();
//Thread id (which is TUint64) is broken in to two TUint32 and written as RWriteStream doesnt
//support TUint64. Client API will recreate TUint64 value from these TUint32 values.
if(id.Id())
{
//RWriteStream there is no API to writes a TUint64 value as a 64 bit value to stream
writeStream.WriteUint32L(I64HIGH(id.Id()));
writeStream.WriteUint32L(I64LOW(id.Id()));
}
}
writeStream.CommitL();
aMessage.WriteL(0, buf->Ptr(0));
CleanupStack::PopAndDestroy(2,buf); //writeStream, buf
}
else
{
aMessage.Write(2, TPckg<TInt>(arrayItemCount));
}
}
/**
This method will return the number of client that are registered for Shutdown notification with ShutDown server.
@return Number of registered clients.
*/
TInt CServShutdownServer::ClientArrayCount()
{
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
TInt count =0;
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
TThreadId id=static_cast<CServShutdownSession*>(session)->ClientThreadId();
//Clients which are not registered for notification will not have RMessage in the session and hence
//thread id will be NULL(e.g.CLafShutdownEventObserverAdaptor). Avoid such clients.
if(id.Id())
{
++count;
}
}
return count;
}
#endif //SYMBIAN_SSM_GRACEFUL_SHUTDOWN
/**
@return Non-zero, if all registered clients have pending requests.
*/
TBool CServShutdownServer::AllSessionsHavePendingRequest() const
{
TBool ret=ETrue;
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
const CServShutdownSession* mySession=static_cast<CServShutdownSession*>(session);
if (!mySession->HasPendingRequest())
{
ret=EFalse;
break;
}
}
return ret;
}
/**
@param aId Client's thread id.
@return Non-zero if the client with this thread id has no pending request.
*/
EXPORT_C TBool CServShutdownServer::IsClientHung(TThreadId aId) const
{
TBool ret=EFalse;
TDblQueIter<CSession2> iter(iSessionIter);
iter.SetToFirst();
for (CSession2* session=iter++; session!=NULL; session=iter++)
{
const CServShutdownSession* mySession=static_cast<CServShutdownSession*>(session);
if (mySession->ClientThreadId()==aId)
{
if (!mySession->HasPendingRequest())
{
ret=ETrue;
break;
}
}
}
return ret;
}
//
// class CServShutdownSession
//
/**
*/
EXPORT_C CServShutdownSession::CServShutdownSession()
: CSession2(),
iCurrentEvent(-1), iOutstandingEvent(-1)
{}
/**
*/
EXPORT_C CServShutdownSession::~CServShutdownSession()
{
}
/**
Standard phase-one factor method for creating CServShutdownSession instances.
@return A pointer to the created CServShutdownSession instance.
@leave KErrNoMemory Not enough memory to complete the operation.
*/
CServShutdownSession* CServShutdownSession::NewL()
{
return new (ELeave) CServShutdownSession;
}
/**
@return Non-zero, if the client has a pending request.
*/
TBool CServShutdownSession::HasPendingRequest() const
{
return !iPtr.IsNull();
}
/**
This method will complete the pending asychronous client request, effectivelly notifying it
about the action, which the client has to do.
@param aSaveType The type of the requested save action.
*/
void CServShutdownSession::NotifySave(MSaveObserver::TSaveType aSaveType)
{
const TInt saveType=(TInt)aSaveType;
if (HasPendingRequest())
{
iPtr.Complete(saveType);
iCurrentEvent=saveType;
}
else
{
if (iCurrentEvent==(TInt)MSaveObserver::ESaveAll ||
(iCurrentEvent==(TInt)MSaveObserver::ESaveQuick &&
aSaveType==MSaveObserver::ESaveData))
{
iOutstandingEvent=saveType;
}
}
}
/**
This method dispatches all client requests to the appropriate method calls.
@param aMessage The client's message
@param aCompleteRequest An output parameter. If zero, the client request
will be completed later.
*/
void CServShutdownSession::DoServiceL(const RMessage2& aMessage, TBool& aCompleteRequest)
{
switch (aMessage.Function())
{
case TSaveOpCodeNotify:
RequestNotifyPowerDown(aMessage);
// don't complete async message yet
aCompleteRequest=EFalse;
break;
case TSaveOpCodeNotifyCancel:
RequestNotifyPowerDownCancel();
break;
case TSaveOpCodeHandleError:
User::Leave(KErrNotSupported);
break;
case TSaveOpCodePowerOff:
PowerOffL(aMessage);
break;
case TSaveOpCodeQueryPowerState:
PowerStateL(aMessage);
break;
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
case EEventObsAdaptHandleShutdown:
case EEventObsAdaptClientArrayCount:
case EEventObsAdaptClientArray:
case EEventObsAdaptIsClientHung:
case EEventObsAdaptGetShutdownState:
{
if (IsSsmGracefulShutdown()) // SSM should be used for device shutdown
{
switch(aMessage.Function())
{
case EEventObsAdaptHandleShutdown:
HandleShutdownEventL(aMessage);
break;
case EEventObsAdaptClientArrayCount:
ClientArrayCount(aMessage);
break;
case EEventObsAdaptClientArray:
ClientArrayL(aMessage);
break;
case EEventObsAdaptIsClientHung:
IsClientHung(aMessage);
break;
case EEventObsAdaptGetShutdownState:
GetShutdownState(aMessage);
break;
}
}
break;
}
#endif // SYMBIAN_SSM_GRACEFUL_SHUTDOWN
default:
User::Leave(KErrNotSupported);
break;
}
}
#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
void CServShutdownSession::HandleShutdownEventL(const RMessage2& aMessage)
{
MSaveObserver::TSaveType action = static_cast <MSaveObserver::TSaveType> (aMessage.Int0());
TBool powerOff = static_cast <TBool> (aMessage.Int1());
TPowerState powerEvent = static_cast <TPowerState> (aMessage.Int2());
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
server->HandleShutdownEventL(action, powerOff, powerEvent);
}
void CServShutdownSession::ClientArrayCount(const RMessage2& aMessage) const
{
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
const TInt count = server->ClientArrayCount();
aMessage.Write(0, TPckg<TInt>(count));
}
/*
This function will write all registered client array in a buffer only when client side array count is equal to server side array count
else aMessage will contain an empty buffer and server side array count .
@param aMessage consists of buffer, clientside array count and server side array count
*/
void CServShutdownSession::ClientArrayL(const RMessage2& aMessage)
{
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
server->ClientArrayL(aMessage);
}
void CServShutdownSession::IsClientHung(const RMessage2& aMessage) const
{
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
TThreadId threadId = static_cast <TThreadId>(aMessage.Int0());
TBool clientHung = server->IsClientHung(threadId);
aMessage.Write(1, TPckg<TBool>(clientHung));
}
void CServShutdownSession::GetShutdownState(const RMessage2& aMessage) const
{
TBool powerOff;
TBool allSessionsHavePendingRequest;
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
server->GetShutdownState(powerOff, allSessionsHavePendingRequest);
aMessage.Write(0, TPckg<TBool>(powerOff));
aMessage.Write(1, TPckg<TBool>(allSessionsHavePendingRequest));
}
#endif //SYMBIAN_SSM_GRACEFUL_SHUTDOWN
/**
Handles the servicing of client requests passed to the shutdown server.
@param aMessage The message containing the client request.
*/
EXPORT_C void CServShutdownSession::ServiceL(const RMessage2& aMessage)
{
TBool aCompleteRequest=ETrue;
TRAPD(error, DoServiceL(aMessage, aCompleteRequest));
if (aCompleteRequest)
{
aMessage.Complete(error);
}
}
/**
This method processes a client-side registration request. It is an asynchronous request,
which will be completed later, when powerdown/low memory event occurs.
@param aMessage The message containing the client request.
*/
void CServShutdownSession::RequestNotifyPowerDown(const RMessage2& aMessage)
{
iCurrentEvent=-1;
iPtr = aMessage;
if (iOutstandingEvent!=-1)
{
NotifySave((MSaveObserver::TSaveType)iOutstandingEvent);
iOutstandingEvent=-1;
}
//The thread variable is just a dummy variable(input for CServShutdownServer::HandlePowerNotifRequest)
//and is not used inside the function at all, it is there to preserve BC.
RThread thread;
static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()))->HandlePowerNotifRequest(thread);
}
/**
This method cancels the client registration, completing the requests with
KErrCancel error code.
*/
void CServShutdownSession::RequestNotifyPowerDownCancel()
{
if (HasPendingRequest())
iPtr.Complete(KErrCancel);
}
/**
@return The client's thread id.
*/
TThreadId CServShutdownSession::ClientThreadId() const
{
TThreadId id = NULL;
RThread clientThread;
//Clients which are not registered for notification will not have RMessage in the session and hence
//thread id will be NULL(e.g.CLafShutdownEventObserverAdaptor). Avoid such clients.
if(!iPtr.IsNull() && KErrNone == iPtr.Client(clientThread))
{
id = clientThread.Id();
}
clientThread.Close();
return id;
}
/**
This method should be used only with SYSLIBS_TEST macro defined and can be used to
initiate a powerdown sequence.
Without SYSLIBS_TEST macro defined the method will panic the client with
KErrNotSupported error code.
@param aMessage The message containing the client request.
*/
#ifdef SYSLIBS_TEST
void CServShutdownSession::PowerOffL(const RMessage2& aMessage)
{
MSaveObserver::TSaveType action = static_cast <MSaveObserver::TSaveType> (aMessage.Int0());
TBool powerOff = static_cast <TBool> (aMessage.Int1());
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
server->HandleShutdownEventL(action, powerOff);
}
#else
void CServShutdownSession::PowerOffL(const RMessage2& aMessage)
{
aMessage.Panic(__SHUTDOWN_SERVER_NAME, KErrNotSupported);
}
#endif//SYSLIBS_TEST
/**
This method should be used only with SYSLIBS_TEST macro defined and can be used to
get the power state of the server.
Without SYSLIBS_TEST macro defined the method will panic the client with
KErrNotSupported error code.
@param aMessage The message containing the client request.
*/
#ifdef SYSLIBS_TEST
void CServShutdownSession::PowerStateL(const RMessage2& aMessage) const
{
CServShutdownServer* server = static_cast<CServShutdownServer*>(const_cast<CServer2*>(Server()));
TBool powerOff = server->IsPowerOff();
TPckg<TBool> powerOffPckg(powerOff);
aMessage.WriteL(0,powerOffPckg);
}
#else
void CServShutdownSession::PowerStateL(const RMessage2& aMessage) const
{
aMessage.Panic(__SHUTDOWN_SERVER_NAME, KErrNotSupported);
}
#endif//SYSLIBS_TEST