diff -r 000000000000 -r 4e1aa6a622a0 resourcemgmt/powerandmemorynotificationservice/src/shutdownsrv.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/resourcemgmt/powerandmemorynotificationservice/src/shutdownsrv.cpp Tue Feb 02 00:53:00 2010 +0200 @@ -0,0 +1,760 @@ +// 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 +#include +#include + +#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN +#include +#include +#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 iter(iSessionIter); + iter.SetToFirst(); + + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + if (!static_cast(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 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(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 type and appends to it the +thread id-s of the all registered clients. +The created CArrayFix instance will be pushed on the cleanup stack. +@return A pointer to a CArrayFix array with the client thread id-s. +@leave Some system-wide error codes including KErrNoMemory. +*/ +EXPORT_C CArrayFix* CServShutdownServer::ClientArrayLC() + { + CArrayFix* clientArray=new(ELeave) CArrayFixFlat(2); + CleanupStack::PushL(clientArray); + TDblQueIter iter(iSessionIter); + iter.SetToFirst(); + TKeyArrayFix key(0,ECmpTInt); + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + TThreadId id=static_cast(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 iter(iSessionIter); + iter.SetToFirst(); + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + TThreadId id=static_cast(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(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 iter(iSessionIter); + iter.SetToFirst(); + TInt count =0; + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + TThreadId id=static_cast(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 iter(iSessionIter); + iter.SetToFirst(); + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + const CServShutdownSession* mySession=static_cast(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 iter(iSessionIter); + iter.SetToFirst(); + for (CSession2* session=iter++; session!=NULL; session=iter++) + { + const CServShutdownSession* mySession=static_cast(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 (aMessage.Int0()); + TBool powerOff = static_cast (aMessage.Int1()); + TPowerState powerEvent = static_cast (aMessage.Int2()); + + CServShutdownServer* server = static_cast(const_cast(Server())); + server->HandleShutdownEventL(action, powerOff, powerEvent); + } + +void CServShutdownSession::ClientArrayCount(const RMessage2& aMessage) const + { + CServShutdownServer* server = static_cast(const_cast(Server())); + const TInt count = server->ClientArrayCount(); + aMessage.Write(0, TPckg(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(const_cast(Server())); + server->ClientArrayL(aMessage); + } + +void CServShutdownSession::IsClientHung(const RMessage2& aMessage) const + { + CServShutdownServer* server = static_cast(const_cast(Server())); + TThreadId threadId = static_cast (aMessage.Int0()); + TBool clientHung = server->IsClientHung(threadId); + + aMessage.Write(1, TPckg(clientHung)); + } + +void CServShutdownSession::GetShutdownState(const RMessage2& aMessage) const + { + TBool powerOff; + TBool allSessionsHavePendingRequest; + + CServShutdownServer* server = static_cast(const_cast(Server())); + server->GetShutdownState(powerOff, allSessionsHavePendingRequest); + aMessage.Write(0, TPckg(powerOff)); + aMessage.Write(1, TPckg(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(const_cast(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 (aMessage.Int0()); + TBool powerOff = static_cast (aMessage.Int1()); + CServShutdownServer* server = static_cast(const_cast(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(const_cast(Server())); + TBool powerOff = server->IsPowerOff(); + TPckg powerOffPckg(powerOff); + aMessage.WriteL(0,powerOffPckg); + } +#else +void CServShutdownSession::PowerStateL(const RMessage2& aMessage) const + { + aMessage.Panic(__SHUTDOWN_SERVER_NAME, KErrNotSupported); + } +#endif//SYSLIBS_TEST +