diff -r 000000000000 -r 2f259fa3e83a uifw/AknGlobalUI/NotifierWrapper/src/AknNotifierWrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uifw/AknGlobalUI/NotifierWrapper/src/AknNotifierWrapper.cpp Tue Feb 02 01:00:49 2010 +0200 @@ -0,0 +1,702 @@ +/* +* Copyright (c) 2004-2007 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: Notifier server app wrapper implementation. +* +*/ + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#include +#endif +#include +#include +#include +#include +#include +#include "AknNotifierWrapper.h" +#include "AknNotifierControllerUtilities.h" +#include +#include + +const TInt KPreLoadEnabledButNotDoneYet(2); + +//------------------------------------------- +// CAknNotifierMessageObserver +//------------------------------------------- + +CAknNotifierMessageObserver::CAknNotifierMessageObserver(TUid aNotifierUid, + MAknNotifierWrapper* aOwner, + const RMessagePtr2& aClientMessage, + TInt aReplySlot) +:CActive(CActive::EPriorityStandard), + iNotifierUid(aNotifierUid), + iReplySlot(aReplySlot), + iOwner(aOwner) + { + iMessage = aClientMessage; + CActiveScheduler::Add(this); + } + + +EXPORT_C CAknNotifierMessageObserver* CAknNotifierMessageObserver::NewServerRequestL( + TUid aNotifierUid, + MAknNotifierWrapper* aOwner, + const RMessagePtr2& aClientMessage, + TInt aReplySlot) + { + CAknNotifierMessageObserver* me = + new (ELeave) CAknNotifierMessageObserver(aNotifierUid,aOwner,aClientMessage,aReplySlot); + + // a bit awkward I admit... + if (!aClientMessage.IsNull()) + { + CleanupStack::PushL(me); + me->iReplyBuf = HBufC8::NewL(aClientMessage.GetDesLength(aReplySlot)); + // we need ptr which keeps its address during async operation + me->iReplyDesc = new (ELeave) TPtr8(me->iReplyBuf->Des()); + CleanupStack::Pop(); + + // just in case: if notifier implementation wants to transmit data via replybuf for + // some reason + aClientMessage.Read(aReplySlot,*(me->iReplyDesc)); + } + + return me; + } + + +CAknNotifierMessageObserver::~CAknNotifierMessageObserver() + { + delete iReplyDesc; + delete iReplyBuf; + delete iInputBuf; + __ASSERT_DEBUG(!IsActive(), User::Invariant() ); + } + +void CAknNotifierMessageObserver::DoCancel() + { + if (iOwner) + { + iOwner->AsyncMessageCompleted( this ); + } + + if (!iMessage.IsNull()) + { + iMessage.Complete(KErrCancel); + } + + User::WaitForRequest(iStatus); + delete this; + } + +void CAknNotifierMessageObserver::RunL() + { + if (iOwner) + {// offer message from app-server for wrapper to clone + iOwner->AsyncMessageCompleted( this ); + } + + // if Owner did not complete the message, do default reply + if (!iMessage.IsNull()) + { + TInt err = iMessage.Write(iReplySlot,iReplyBuf->Des()); + iMessage.Complete(err?err:iStatus.Int()); + } + delete this; + } + +//------------------------------------------- +// MAknNotifierWrapper +//------------------------------------------- + +// Default implementation to avoid SC break. +EXPORT_C void MAknNotifierWrapper::UpdateNotifierL( + TUid /*aNotifierUid*/, + const TDesC8& /*aBuffer*/, + TInt /*aReplySlot*/, + const RMessagePtr2& /*aMessage*/ ) + { + User::Leave( KErrNotSupported ); + } + +//------------------------------------------- +// CAknNotifierWrapperLight +//------------------------------------------- +EXPORT_C CAknNotifierWrapperLight::~CAknNotifierWrapperLight() + { + } + +EXPORT_C CAknNotifierWrapperLight::CAknNotifierWrapperLight( + MAknNotifierWrapper& aSessionOwningNotifier, + TUid aNotifierUid, + TUid aChannel, + TInt aPriority ) +:iOwner(aSessionOwningNotifier) + { + iInfo.iUid = aNotifierUid; + iInfo.iChannel = aChannel; + iInfo.iPriority = aPriority; + } + + +EXPORT_C TPtrC8 CAknNotifierWrapperLight::UpdateL(const TDesC8& aBuffer) + { + return iOwner.UpdateNotifierL( iInfo.iUid, aBuffer ); + } + +EXPORT_C void CAknNotifierWrapperLight::UpdateL(const TDesC8& aBuffer, TInt aReplySlot, + const RMessagePtr2& aMessage) + { + iOwner.UpdateNotifierL( iInfo.iUid, aBuffer, aReplySlot, aMessage ); + } + +EXPORT_C void CAknNotifierWrapperLight::Cancel() + { + iOwner.CancelNotifier(iInfo.iUid); + } + +EXPORT_C void CAknNotifierWrapperLight::StartL(const TDesC8& aBuffer, TInt aReplySlot, + const RMessagePtr2& aMessage) + { + iOwner.StartNotifierL(iInfo.iUid, aBuffer, aReplySlot, aMessage); + } + +EXPORT_C TPtrC8 CAknNotifierWrapperLight::StartL(const TDesC8& aBuffer) + { + return iOwner.StartNotifierL(iInfo.iUid, aBuffer); + } + +EXPORT_C MEikSrvNotifierBase2::TNotifierInfo CAknNotifierWrapperLight::Info() const + { + return iInfo; + } + +EXPORT_C MEikSrvNotifierBase2::TNotifierInfo CAknNotifierWrapperLight::RegisterL() + { + return iInfo; + } + +EXPORT_C void CAknNotifierWrapperLight::Release() + { + Cancel(); + delete this; + } + +EXPORT_C TInt CAknNotifierWrapperLight::NotifierCapabilites() + { + return MEikSrvNotifierBase2::NotifierCapabilites(); + } + +//------------------------------------------- +// CAknNotifierWrapper +//------------------------------------------- + +EXPORT_C CAknNotifierWrapper::CAknNotifierWrapper(TUid aNotifierUid, TUid aChannel, TInt aPriority) + :CAknNotifierWrapperLight(*this, aNotifierUid, aChannel, aPriority) + { + iClient.SetServerAppUid(AppServerUid()); + } + +CAknNotifierWrapper::CAknNotifierWrapper(TUid aNotifierUid, TUid aChannel, TInt aPriority, + TUid aAppServerUid) +:CAknNotifierWrapperLight(*this, aNotifierUid, aChannel, aPriority), iAppServerUid(aAppServerUid) + { + iClient.SetServerAppUid(iAppServerUid); + } + + +EXPORT_C CAknNotifierWrapper::~CAknNotifierWrapper() + { + CEikServAppUi* appUi = (CEikServAppUi*)CCoeEnv::Static()->AppUi(); + if (appUi && appUi->NotifierController()) + { + appUi->NotifierController()->RegisterNotifierControllerPlugin(this, ETrue); + } + + delete iReplyBuffer; + + // Make sure that there are no pending messages + __ASSERT_DEBUG(iMessageQueue.Count()==0, User::Invariant()); + + iClient.Close(); + } + +// We use CEikServAppUi idle queue to launch application servers (applications will be started after +// eiksrv-construction has been completed ) +EXPORT_C MEikSrvNotifierBase2::TNotifierInfo CAknNotifierWrapper::RegisterL() + { + CEikServAppUi* appUi = (CEikServAppUi*)CCoeEnv::Static()->AppUi(); + if (appUi) + { + if (AppServerUid()!=KAknCapServerUid) // aknCapServer is handled elsewhere + { + appUi->StartNewServerApplicationL(AppServerUid()); + } + + appUi->NotifierController()->RegisterNotifierControllerPlugin(this, EFalse); + } + return CAknNotifierWrapperLight::RegisterL(); + } + + +EXPORT_C TPtrC8 CAknNotifierWrapper::UpdateNotifierL( TUid aNotifierUid, const TDesC8& aBuffer) + { + User::LeaveIfError(iClient.SendSync(EUpdateNotifier, aNotifierUid, aBuffer, + SynchronousReplyBuf(), KNullDesC)); + + return TPtrC8(SynchronousReplyBuf()); + } + +EXPORT_C void CAknNotifierWrapper::UpdateNotifierL( TUid aNotifierUid, const TDesC8& aBuffer, + TInt aReplySlot, const RMessagePtr2& aMessage) + { + CAknNotifierMessageObserver* entry = + CreateNewQueueEntryL(aNotifierUid, aBuffer, aReplySlot, aMessage); + + if (entry) + { + iClient.SendAsync(EUpdateNotifierAndGetResponse, aNotifierUid,*entry->iInputBuf, entry, + KNullDesC); + } + } + +void CAknNotifierWrapper::CompleteOutstandingRequests( TUid aNotifierUid, TInt aReason ) + { + for ( TInt i = 0; i < iMessageQueue.Count(); i++ ) + { + if (iMessageQueue[i]->iNotifierUid == aNotifierUid) + { + // This leaves listener to active state, it will be destroyed when a message in + // Notifier server application is completed. + // Effectively this means that plugin can't send any data inside message which is + // being cancelled. + if (!iMessageQueue[i]->iMessage.IsNull()) + { + iMessageQueue[i]->iMessage.Complete(aReason); + } + } + } + } + +EXPORT_C void CAknNotifierWrapper::CancelNotifier( TUid aNotifierUid ) + { + iClient.SendSync(ECancelNotifier, aNotifierUid, KNullDesC8, SynchronousReplyBuf(), KNullDesC); + CompleteOutstandingRequests( aNotifierUid, KErrCancel); + } + + +CAknNotifierMessageObserver* CAknNotifierWrapper::CreateNewQueueEntryL(TUid aNotifierUid, + const TDesC8& aBuffer, TInt aReplySlot, const RMessagePtr2& aMessage) + { + CAknNotifierMessageObserver* entry = 0; + if (!aMessage.IsNull()) + { + entry = CAknNotifierMessageObserver::NewServerRequestL(aNotifierUid, this, aMessage, + aReplySlot); + + CleanupStack::PushL(entry); + entry->iInputBuf = aBuffer.AllocL(); + CleanupStack::Pop(); + TInt err = iMessageQueue.Append(entry); + if (err) + { // completes client message with err; + TRequestStatus* ptr = &entry->iStatus; + User::RequestComplete(ptr, err); + entry = 0; + } + } + return entry; + } + +EXPORT_C void CAknNotifierWrapper::StartNotifierL( TUid aNotifierUid, const TDesC8& aBuffer, + TInt aReplySlot, const RMessagePtr2& aMessage) + { + CAknNotifierMessageObserver* entry = + CreateNewQueueEntryL(aNotifierUid, aBuffer, aReplySlot, aMessage); + + if (entry) + { + iClient.SendAsync( + EStartNotifierAndGetResponse, + aNotifierUid, + *entry->iInputBuf, + entry, + KNullDesC); + } + } + +EXPORT_C TPtrC8 CAknNotifierWrapper::StartNotifierL( TUid aNotifierUid, const TDesC8& aBuffer) + { + User::LeaveIfError(iClient.SendSync(EStartNotifier, aNotifierUid, aBuffer, + SynchronousReplyBuf(), KNullDesC)); + + return TPtrC8(SynchronousReplyBuf()); + } + +// Just remove completed entry from queue, default completion (ie. just copying message from +// app server) is currently enough for us. +EXPORT_C void CAknNotifierWrapper::AsyncMessageCompleted( CAknNotifierMessageObserver* aEntry ) + { + TInt status = aEntry->iStatus.Int(); + + TInt index = iMessageQueue.Find(aEntry); + if (index != KErrNotFound) + { + iMessageQueue.Remove(index); + } + + if ( status == KErrServerTerminated && AppServerUid() != KCommonNotifierAppSrvUid) + { // Just try to restart (connect on next request), not interested if success or fail + // + // We could clone the original message here and resend it to server + // if information was really vital for system + Client().Close(); + Client().SetHandle(0); + + TRAP_IGNORE(((CEikServAppUi*)CCoeEnv::Static()->AppUi())->StartNewServerApplicationL( + AppServerUid())); + } + } + +EXPORT_C CAknNotifierWrapper* CAknNotifierWrapper::NewL( + TUid aNotifierUid, + TUid aChannel, + TInt aPriority, + TUid aAppServerUid, + TUint aReplyBufSize) + { + CAknNotifierWrapper* me = + new (ELeave) CAknNotifierWrapper(aNotifierUid, aChannel, aPriority, aAppServerUid); + + CleanupStack::PushL(me); + me->ConstructL(aReplyBufSize); + CleanupStack::Pop(); + return me; + } + +EXPORT_C void CAknNotifierWrapper::DoNotifierControllerCommand(TInt aCommand) + { + iClient.SendAsync(aCommand); + } + +void CAknNotifierWrapper::ConstructL(TUint aReplyBufSize) + { + iReplyBuffer = HBufC8::NewL(aReplyBufSize); + } + +RAknNotifierAppServClient::RAknNotifierAppServClient() + :iAppServerUid(KNullUid), iOwnerUsingMonitor(0) + { + } + +void RAknNotifierAppServClient::SetServerAppUid(TUid aAppServerUid) + { + iAppServerUid = aAppServerUid; + } + +TInt RAknNotifierAppServClient::StartServer() + { + TRAPD(ret, StartServerL()) + return ret; + } + +void RAknNotifierAppServClient::StartServerL() + { + if (Handle() != 0) + { + return; + } + + if (iAppServerUid == KNullUid) + { + User::Leave(KErrGeneral); + } + + _LIT(KServerNameFormat, "%08x_%08x_AppServer"); + TFullName serverName; + serverName.Format(KServerNameFormat, KUikonUidPluginInterfaceNotifiers, iAppServerUid); + TFindServer find(serverName); + TFullName fullName; + if (find.Next(fullName) == KErrNone) + { + ConnectExistingByNameL(serverName); + if (iOwnerUsingMonitor) + { + iOwnerUsingMonitor->AppServerConnectedL(); + } + return; + } + + User::Leave(KErrNotReady); + } + +TUid RAknNotifierAppServClient::ServiceUid() const + { + return KAknNotifierServiceUid; + } + +TInt RAknNotifierAppServClient::SendSync(TNotifierMessage aFunction, TUid aNotifierUid, + const TDesC8& aBuffer, TPtr8 aReply, const TDesC& aLibraryName) + { + // just an optimisation + if (aFunction == ECancelNotifier && !Handle()) + { + return KErrNone; + } + + TInt err = StartServer(); + return err ? err : SendReceive(aFunction, TIpcArgs((TInt)aNotifierUid.iUid, &aBuffer, &aReply, + &aLibraryName)); + } + +void RAknNotifierAppServClient::SendAsync(TNotifierMessage aFunction, TUid aNotifierUid, + const TDesC8& aBuffer, CAknNotifierMessageObserver* aQueueEntry, const TDesC& aLibraryName) + { + TInt err = StartServer(); + + aQueueEntry->Start(); + if (err) + { + TRequestStatus* sptr= &aQueueEntry->iStatus; + User::RequestComplete(sptr, err); + return; + } + // we implicitely trust that aLibraryName will exist when data is extracted at other end... + SendReceive( + aFunction, + TIpcArgs((TInt)aNotifierUid.iUid, &aBuffer, aQueueEntry->iReplyDesc, &aLibraryName), + aQueueEntry->iStatus); + } + +TInt RAknNotifierAppServClient::SendAsync(TInt aNotifierControllerCommand) + { + if (Handle()!=0) + { + return Send(KDoNotifierControllerCommand, TIpcArgs(aNotifierControllerCommand)); + } + + // omit error as there is no need to forward commands to server apps which are not running yet. + return KErrNone; + } + +EXPORT_C CAknCommonNotifierWrapper* CAknCommonNotifierWrapper::NewL( + TUid aNotifierUid, + TUid aChannel, + TInt aPriority, + const TDesC& aLibraryName, + TUint aReplyBufSize, + TBool aPreLoad) + { + CAknCommonNotifierWrapper* me = + new (ELeave) CAknCommonNotifierWrapper(aNotifierUid, aChannel, aPriority, aPreLoad); + + CleanupStack::PushL(me); + me->ConstructL(aLibraryName, aReplyBufSize); + CleanupStack::Pop(); + return me; + } + +void CAknCommonNotifierWrapper::ConstructL(const TDesC& aLibraryName, TUint aReplyBufSize) + { + SetSynchReplybuf(HBufC8::NewL(aReplyBufSize)); + iLibraryName = aLibraryName.AllocL(); + Client().SetOwnerUsingMonitor(this); + } + +CAknCommonNotifierWrapper::CAknCommonNotifierWrapper( + TUid aNotifierUid, + TUid aChannel, + TInt aPriority, + TBool aPreLoad) +:CAknNotifierWrapper(aNotifierUid, aChannel, aPriority), iPreLoad(aPreLoad) + { + Client().SetServerAppUid(AppServerUid()); + if (aPreLoad) + { + iPreLoad = KPreLoadEnabledButNotDoneYet; + } + } + +EXPORT_C void CAknCommonNotifierWrapper::SetCustomSecurityHandler( + MAknNotifierCustomSecurityCheck* aHandler) + { + iCustomSecurityCheck = aHandler; + } + +CAknCommonNotifierWrapper::~CAknCommonNotifierWrapper() + { + delete iMonitor; + delete iIdle; + if (iCustomSecurityCheck) + { + iCustomSecurityCheck->Release(); + } + delete iLibraryName; + iPendingArray.Close(); + } + +void CAknCommonNotifierWrapper::StartNotifierL( TUid aNotifierUid, const TDesC8& aBuffer, + TInt aReplySlot, const RMessagePtr2& aMessage) + { + if (iPreLoad == KPreLoadEnabledButNotDoneYet) // only preload libs to support message queue + { + iPendingArray.AppendL(TPendingMsg(aNotifierUid, aMessage)); + return; + } + + if (iCustomSecurityCheck) + { + iCustomSecurityCheck->CustomSecurityCheckL(aMessage); + } + + CAknNotifierMessageObserver* entry = + CreateNewQueueEntryL(aNotifierUid, aBuffer, aReplySlot, aMessage); + + if (entry) + { + Client().SendAsync( + EStartNotifierAndGetResponse, + aNotifierUid, + *entry->iInputBuf, + entry, + *iLibraryName); + } + } + +TPtrC8 CAknCommonNotifierWrapper::StartNotifierL( TUid aNotifierUid, const TDesC8& aBuffer) + { + if (iPreLoad == KPreLoadEnabledButNotDoneYet) + { + // In case of synchronous commands (start / update) we cannot delay sending messages, + // only way to recover is to return error. Update cannot occur before one successfull + // start so it does need changes. + User::Leave(KErrNotReady); + } + + User::LeaveIfError(Client().SendSync(EStartNotifier, aNotifierUid, aBuffer, + SynchronousReplyBuf(), *iLibraryName)); + + return TPtrC8(SynchronousReplyBuf()); + } + +TBool CallBackLoad(TAny* aThis) + { + return ((CAknCommonNotifierWrapper*)aThis)->PreLoadLibrary(); + } + +EXPORT_C void CAknCommonNotifierWrapper::PreLoadLibraryL() + { + iPreLoad = ETrue; // restore original, real boolean value once callback from server arrives + if (!iIdle) + { + iIdle = CIdle::NewL(CActive::EPriorityIdle); + iIdle->Start(TCallBack(CallBackLoad, this)); + } + } + +// On succes this method will cause unbalanced reference count in app server -> library will not be +// unloaded unless server terminates. +TBool CAknCommonNotifierWrapper::PreLoadLibrary() + { + TInt err = Client().SendSync( + (TNotifierMessage)EAknNfySrvLoadLibrary, + KNullUid, + KNullDesC8, + SynchronousReplyBuf(), + *iLibraryName); + + if (err == KErrNotReady) + { + return ETrue; + } + else + { + delete iIdle; + iIdle = 0; + return EFalse; + } + } + +MEikSrvNotifierBase2::TNotifierInfo CAknCommonNotifierWrapper::RegisterL() + { + if (iPreLoad) + { + CEikServAppUi* appUi = (CEikServAppUi*)CCoeEnv::Static()->AppUi(); + if (appUi) + { + appUi->NotifierController()->RegisterPreloadPluginL(this); + } + } + return CAknNotifierWrapper::RegisterL(); + } + + +void CAknCommonNotifierWrapper::HandleServerAppExit(TInt) + { + delete iMonitor; + iMonitor = 0; + Client().Close(); + Client().SetHandle(0); + if (iPreLoad) + { + iPreLoad = KPreLoadEnabledButNotDoneYet; + } + } + +void CAknCommonNotifierWrapper::CancelNotifier( TUid aNotifierUid ) + { + // if there are pending requests, there cannot be actual connection to server yet + if (iPendingArray.Count()) + { + for (TInt i = iPendingArray.Count()-1; i >= 0; i--) + { + if (iPendingArray[i].iUid == aNotifierUid.iUid) + { + iPendingArray.Remove(i); + } + } + } + else + { + CAknNotifierWrapper::CancelNotifier( aNotifierUid ); + } + } + +void TryProcessPendingEntryL(CAknCommonNotifierWrapper::TPendingMsg& aPendingEntry, + CAknCommonNotifierWrapper* aThis) + { + HBufC8* buf = HBufC8::NewLC(aPendingEntry.iMessage.GetDesLengthL(1)); + TPtr8 ptr = buf->Des(); + aPendingEntry.iMessage.ReadL(1, ptr ); + aThis->StartNotifierL(TUid::Uid(aPendingEntry.iUid), *buf, 2, aPendingEntry.iMessage ); + CleanupStack::PopAndDestroy(); + } + +void CAknCommonNotifierWrapper::AppServerConnectedL() + { + if (!iMonitor) + { + iMonitor = CApaServerAppExitMonitor::NewL(Client(), *this, CActive::EPriorityStandard); + } + + for (TInt i = 0; i < iPendingArray.Count(); i++) + { // common app servers have no problem with asynch message slots + // anyway, we don't allow pending messages to abort StartServerL so trap needed + TRAP_IGNORE(TryProcessPendingEntryL(iPendingArray[i], this)); + } + + iPendingArray.Reset(); + } + +// End of file