--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/commsfwsupport/commselements/rootserver/bindmgr/bm_module.cpp Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,661 @@
+// Copyright (c) 2003-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:
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <cflog.h>
+#include <commschan.h>
+#include <cfshared.h>
+using namespace CommsFW;
+#include "bm_defs.h"
+
+
+#ifdef _DEBUG
+// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module
+// (if it could happen through user error then you should give it an explicit, documented, category + code)
+_LIT(KSpecAssert_ElemRootServerbm, "ElemRootServerbm");
+#endif
+
+CCommsChannelSendQueue* CCommsChannelSendQueue::NewL(RCFChannel& aChannel)
+/**
+Generates a new CCommsChannelSendQueue object
+@param aChannel the send channel on which to send messages
+@return A ptr to the new CCommsChannelSendQueue
+@leave KErrNoMemory
+*/
+ {
+ CCommsChannelSendQueue* This =
+ new(ELeave) CCommsChannelSendQueue(aChannel);
+ CActiveScheduler::Add(This);
+ return This;
+ }
+
+
+TInt CCommsChannelSendQueue::Send(const TCFMessage& aMsg)
+/**
+ Send method either sends the requested message immediately or stores it
+internally to send when space is available
+param aMsg the message to be sent
+@return A standard error code indicating success or failure
+*/
+ {
+ TInt ret = KErrNone;
+
+ if (IsActive())
+ {
+ // currently busy waiting for space
+ ret = iMsgQueue.Append(aMsg);
+ }
+ else
+ {
+ __ASSERT_DEBUG(iMsgQueue.Count() == 0, User::Panic(KBindMgrPanic, EMsgQueueFault));
+ ret = iChannel.Send(aMsg);
+ if (KErrOverflow == ret)
+ {
+ ret = iMsgQueue.Append(aMsg);
+ if (KErrNone == ret)
+ {
+ iChannel.NotifySpaceAvailable(iStatus);
+ SetActive();
+ }
+ }
+ }
+
+ return ret;
+ }
+
+
+TInt CCommsChannelSendQueue::ReserveSpace()
+/**
+Called to make sure a message can be put on the queue at this time in
+this context. For the purpose it is used it would be a nice optimization
+to also check whether there is actually room in the output channel first
+as well, as the space then wouldn't be needed.
+*/
+ {
+
+ // Add dummy message to the queue, to make sure there is space
+ TCFMessage msg;
+ TInt ret = iMsgQueue.Append(msg);
+ if(KErrNone!=ret)
+ {
+ return ret;
+ }
+ iMsgQueue.Remove(iMsgQueue.Count()-1);
+ return KErrNone;
+ }
+
+
+void CCommsChannelSendQueue::RunL()
+/**
+Active object RunL method is called when space becomes available on the send
+queue. Attempts to send the first message in the queue.
+@leave any code from RCFChannel.Send() except KErrOverflow
+*/
+ {
+ __CFLOG_1(KLogSubSysRS, KLogCode,
+ _L8("CCommsChannelSendQueue::RunL &iStatus %08X"), &iStatus);
+ switch(iStatus.Int())
+ {
+ case KErrNone:
+ {
+ while (iMsgQueue.Count() > 0)
+ {
+ TInt ret = iChannel.Send(iMsgQueue[0]);
+ if (KErrNone == ret)
+ {
+ iMsgQueue.Remove(0);
+ }
+ else if (KErrOverflow == ret)
+ {
+ iChannel.NotifySpaceAvailable(iStatus);
+ SetActive();
+ break;
+ }
+ else
+ {
+ User::Leave(ret);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+void CCommsChannelSendQueue::DoCancel()
+/** Cancel CCommsChannelSendQueue NotifySpaceAvailable().
+*/
+ {
+ iChannel.CancelSpaceAvailable();
+ }
+
+CCommsChannelSendQueue::CCommsChannelSendQueue(RCFChannel &aChannel)
+ : CActive(EPriorityStandard),
+ iChannel(aChannel)
+/** Constructor for CCommsChannelSendQueue
+*/
+ {
+ }
+
+CCommsChannelSendQueue::~CCommsChannelSendQueue()
+/** Destructor for CCommsChannelSendQueue
+*/
+ {
+ Cancel();
+ iMsgQueue.Close();
+ }
+
+CModule* CModule::NewL(MBindManagerNotify *aNotifier,
+ const TCFModuleNameF &aModule,
+ TransactionId &aTransactionId,
+ const RCFChannel::TMsgQueues& aInputQueues,
+ const RCFChannel::TMsgQueues& aOutputQueues)
+/** Generates a new CModule object
+@param aNotifier bindmanager object to report events to
+@param aModule name of module
+@param aTransactionId xx
+@param aInputQueues queues on which messages should sent to the module
+@param aOutputQueues queues on which messages will be received from the module
+@return A ptr to the new CModule
+@leave KErrNoMemory
+*/
+ {
+ CModule* pM = new (ELeave) CModule(aNotifier, aTransactionId, aModule);
+ CleanupStack::PushL(pM);
+ pM->ConstructL(aInputQueues, aOutputQueues);
+ CleanupStack::Pop(pM);
+ __CFLOG_STMT(TBuf8<KCFMaxModuleName> buf1;);
+ __CFLOG_STMT(buf1.Copy(aModule););
+ __CFLOG_2(KLogSubSysRS, KLogCode,
+ _L8("CModule::NewL module %S, &iStatus %08X"), &buf1, &(pM->iStatus));
+ return pM;
+ }
+
+void CModule::ConstructL(const RCFChannel::TMsgQueues& aInputQueues,
+ const RCFChannel::TMsgQueues& aOutputQueues)
+/** Second phase constructor for CModule
+@param aInputQueues queues on which messages should sent to the module
+@param aOutputQueues queues on which messages will be received from the module
+@leave KErrNoMemory
+*/
+ {
+ User::LeaveIfError(iSendChannel.Create(aInputQueues, EOwnerProcess));
+ User::LeaveIfError(iRecvChannel.Create(aOutputQueues, EOwnerProcess));
+
+ iDiscoveryArray = new(ELeave) TCFSubModuleNameF[KDefaultSubmoduleArraySize];
+ iSendQueue = CCommsChannelSendQueue::NewL(iSendChannel);
+
+ // make us active
+ CActiveScheduler::Add(this);
+ iRecvChannel.NotifyDataAvailable(iStatus);
+ SetActive();
+ }
+
+void CModule::DoCancel()
+/** Cancel CModule NotifyDataAvailable
+*/
+ {
+ iRecvChannel.CancelDataAvailable();
+ }
+
+TInt CModule::ReserveSpaceInSendQueue()
+ {
+ return iSendQueue->ReserveSpace();
+ }
+
+void CModule::RunL()
+/** Active object RunL method, called when data becomes available on the receive
+queue
+@leave
+*/
+ {
+ __CFLOG_2(KLogSubSysRS, KLogCode,
+ _L8("CModule::RunL &iStatus %08X, status %d"), &iStatus, iStatus.Int());
+
+ switch(iStatus.Int())
+ {
+ case KErrNone:
+ {
+ TCFMessage newMsg;
+ TInt ret = iRecvChannel.Receive(newMsg);
+ __CFLOG_1(KLogSubSysRS, KLogEvent,
+ _L8("CModule::RunL recv returned %d"), ret);
+ if (ret == KErrUnderflow)
+ {
+ break;
+ }
+
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ if (pTask->ReplyMatches(newMsg))
+ {
+ break;
+ }
+ }
+
+ if (pTask)
+ {
+ __CFLOG_2(KLogSubSysRS, KLogCode, _L8("CModule::RunL task found (code %d, id %d)"), newMsg.Code(), *((TId*)newMsg.ParamBlock()) );
+ TBool finished = ETrue;
+ TRAPD(result, finished = CheckDiscoveryMsgFinishedL( newMsg ) );
+ if (result<KErrNone)
+ {
+ // it left, probably KErrNoMemory
+ // so complete the message with this
+ iNotifier->TaskCompleted(*this, *pTask, result);
+ delete pTask;
+ }
+ else if (finished)
+ {
+ TInt status;
+ CTask::GetMsgReturnCode(newMsg, status);
+ // discovery messages overload the return code, to return
+ // the number of submodulenames they wrote into the message
+ // therefore a status retrieved that is greater than 0, implies
+ // we should complete the request with KErrNone.
+ if(status >= 0)
+ {
+ iNotifier->TaskCompleted(*this, *pTask, KErrNone);
+ }
+ else
+ iNotifier->TaskCompleted(*this, *pTask, status);
+ delete pTask;
+ }
+ }
+ else
+ {
+ __CFLOG_2(KLogSubSysRS, KLogCode,
+ _L8("CModule::RunL matching task NOT found (code %d, id %d)"), newMsg.Code(), *((TId*)newMsg.ParamBlock()) );
+ iNotifier->ReceivedMessage(Name(), newMsg);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ iRecvChannel.NotifyDataAvailable(iStatus);
+ SetActive();
+ }
+
+void CModule::Dequeue()
+/** Remove this from the linked list
+*/
+ {
+ iLink.Deque();
+ }
+
+const TCFModuleNameF& CModule::Name() const
+/** Accessor method for the module name
+@return The name
+*/
+ {
+ return iName;
+ }
+
+const TBool& CModule::Initialising() const
+/** Accessor method for the module initialisation state
+@return True if module is still initialising
+*/
+ {
+ return iInitialising;
+ }
+
+void CModule::SetInitialising(const TBool aInitialising)
+/** Set method for the module initialisation state
+@param aInitialising set true if module is initialising
+*/
+ {
+ iInitialising = aInitialising;
+ }
+
+CModule::CModule(MBindManagerNotify *aNotifier,
+ TransactionId &aTransactionId,
+ const TCFModuleNameF &aModule) :
+ CActive(EPriorityStandard),
+ iNotifier(aNotifier),
+ iName(aModule),
+ iInitialising(ETrue),
+ iTaskList(_FOFF(CTask, iLink)),
+ iTransactionId(aTransactionId)
+/** Constructor for CModule
+@param aNotifier bindmanager object to report events to
+@param aTransactionId Generator of unique transaction ids
+@param aModule the name of the module
+*/
+ {
+ RS_DETLOG((KLogSubSysRS, KLogEvent, _L8("CModule::CModule(%X %S)"), this, &iName));
+ }
+
+void CModule::SendL(const TCFMessage &aMessage)
+/** Public method to send a message to a comms server or CPM
+@param aMessage the message to be sent
+@leave KErrNoMemory
+*/
+ {
+ // put msg on send queue
+ User::LeaveIfError(iSendQueue->Send(aMessage));
+ }
+
+void CModule::DoDiscoverL(TUint &aTaskId)
+/** Creates a task to send a discovery request to the module and sends the
+message itself
+@param aTaskId filled in with the id of the new task
+@leave KErrNoMemory
+*/
+ {
+
+ TCFDiscoverMsg discoverMsg(GetNextTransactionId(), ETrue, KDefaultSubmoduleArraySize, iDiscoveryArray);
+ CTask* pTask = CTask::NewL(discoverMsg, discoverMsg.Identifier());
+ CleanupStack::PushL(pTask);
+ aTaskId = pTask->TaskId();
+
+ // put msg on send queue
+ SendL(discoverMsg);
+
+ CleanupStack::Pop(pTask);
+ iTaskList.AddLast(*pTask);
+ }
+
+
+TBool CModule::GetBindTaskL(const RCFChannel::TMsgQueues& aInputQueues,
+ const RCFChannel::TMsgQueues& aOutputQueues,
+ const TCFSubModuleAddress& aSubModule1,
+ const TCFSubModuleAddress& aSubModule2,
+ const TCFBindType aType,
+ CTask*& aTask)
+/** Either creates a task to send a bind request or returns an existing task
+if a matching one already exists
+@param aInputQueues queues for comms between the modules being bound
+@param aOutputQueues queues for comms between the modules being bound
+@param aSubModule1 first submodule being bound
+@param aSubModule2 second submodule being bound
+@param aType the type of binding
+@param aTask set to point to the task
+@return Etrue if a new task was created
+@leave KErrNoMemory
+*/
+ {
+ TBool newTask = EFalse;
+
+ // see if this task already exists
+ TCFBindMsg bindtest(0, aInputQueues, aOutputQueues, &aSubModule1, &aSubModule2, aType);
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ if (pTask->CheckIfSame(bindtest))
+ {
+ break;
+ }
+ }
+
+ // need to create a new one
+ if (!pTask)
+ {
+ TCFBindMsg bindmsg(GetNextTransactionId(), aInputQueues, aOutputQueues, &aSubModule1, &aSubModule2, aType);
+ pTask = CTask::NewL(bindmsg, bindmsg.Identifier());
+ newTask = ETrue;
+ }
+
+ aTask = pTask;
+ return newTask;
+ }
+
+
+TBool CModule::GetUnbindTaskL(const TCFSubModuleAddress& aSubModule1,
+ const TCFSubModuleAddress& aSubModule2,
+ const TBool aPeerIsDead,
+ CTask*& aTask)
+/** Either creates a task to send an unbind request or returns an existing task
+if a matching one already exists
+@param aSubModule1 first submodule being unbound
+@param aSubModule2 second submodule being unbound
+@param aPeerIsDead indicates whether the second module is dead or not
+@param aTask the task
+@return Etrue if a new task was created
+@leave KErrNoMemory
+*/
+ {
+ TBool newTask = EFalse;
+
+ // see if this task already exists
+ TCFUnbindMsg unbindtest(0, &aSubModule1, &aSubModule2, aPeerIsDead);
+
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ if (pTask->CheckIfSame(unbindtest))
+ {
+ break;
+ }
+ }
+
+ if (!pTask)
+ {
+ TCFUnbindMsg unbindmsg(GetNextTransactionId(), &aSubModule1, &aSubModule2, aPeerIsDead);
+ pTask = CTask::NewL(unbindmsg, unbindmsg.Identifier());
+ newTask = ETrue;
+ }
+
+ aTask = pTask;
+ return newTask;
+ }
+
+void CModule::DoStartTaskL(CTask* aTask)
+/** Sends the task's message and adds it the the task in progress list
+@param aTask the task to process
+@leave KErrNoMemory
+*/
+ {
+ __ASSERT_DEBUG(aTask, User::Panic(KSpecAssert_ElemRootServerbm, 1)); // Reassures LINT's ptr tracking
+ SendL(aTask->Message());
+ iTaskList.AddLast(*aTask);
+ }
+
+
+TInt CModule::EnumerateSubModules(const TInt& aPosition, TCFSubModuleNameF& aModuleName)
+/** Enumerate all known submodules.
+@param aPosition The position in the array of submodulenames.
+@param aModuleName The name to be returned
+@return Error code.
+*/
+ {
+ if(aPosition>=iSubModuleNames.Count())
+ return KErrEof;
+ aModuleName = iSubModuleNames[aPosition];
+ return KErrNone;
+ }
+
+TInt CModule::NumSubModules() const
+/** Returns the number of submodules within this module
+@return Number of submodules within this module
+*/
+ {
+ return iSubModuleNames.Count();
+ }
+
+
+TBool CModule::HasSubModule(const TCFSubModuleNameF &aSubModule) const
+/** Searches for a specified submodule name within this module
+@param aSubModule the name of the submodule to search for
+@return Etrue if the submodule is found
+*/
+ {
+ TInt i;
+ TBool found = EFalse;
+ for (i=0; i<iSubModuleNames.Count(); i++)
+ {
+ if (iSubModuleNames[i] == aSubModule)
+ {
+ found = ETrue;
+ break;
+ }
+ }
+ return found;
+ }
+
+
+const TCFSubModuleAddress& CModule::SubModuleNameRefL(const TCFSubModuleAddress& aSubModule)
+/** Provides a reference to a local copy of the submodule name passed as a
+parameter which is guaranteed to last the lifetime of the cmodule
+@param aSubModule the name of the submodule
+@return Reference to the internal submodule address
+@leave KErrNoMemory
+*/
+ {
+ TInt i;
+ TBool found = EFalse;
+ for (i=0; i<iSubModuleAddrs.Count(); i++)
+ {
+ if (iSubModuleAddrs[i] == aSubModule)
+ {
+ found = ETrue;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ User::LeaveIfError(iSubModuleAddrs.Append(aSubModule));
+ }
+
+ return iSubModuleAddrs[i];
+ }
+
+
+void CModule::EndTask(const TUint &aTaskId, const TInt aStatus)
+/** Called by bindmanager to indicate that the task with the specified id has
+completed
+@param aTaskId id of the finished task
+@param aStatus the status code with which the task has ended
+*/
+ {
+ RS_DETLOG((KLogSubSysRS, KLogEvent, _L8("CModule(%X - %S)::EndTask(%d, %d)"), this, &iName, aTaskId, aStatus));
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ if (pTask->TaskId() == aTaskId)
+ {
+ break;
+ }
+ }
+
+ if (pTask)
+ {
+ iNotifier->TaskCompleted(*this, *pTask, aStatus);
+ delete pTask;
+ // note if a reply arrives now, it's too late! msg should be discarded
+ }
+ }
+
+void CModule::EndAllTasks(const TInt aStatus)
+/** Called by bindmanager to end all tasks for this module. Called on receipt of a
+moduleEnded request
+@param aStatus the status code with which the tasks will be ended
+*/
+ {
+ __CFLOG_1(KLogSubSysRS, KLogEvent, _L8("CModule::EndAllTasks - cancelling any outstanding tasks for %S"), &iName);
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ iNotifier->TaskCompleted(*this, *pTask, aStatus);
+ delete pTask;
+ }
+ }
+
+CModule::~CModule()
+/** Destructor for CModule
+*/
+ {
+ RS_DETLOG((KLogSubSysRS, KLogEvent, _L8("CModule::~CModule(%X %S)"), this, &iName));
+ Cancel();
+ Dequeue();
+ delete iSendQueue;
+ if(iDiscoveryArray)
+ {
+ delete[] iDiscoveryArray;
+ }
+
+ iSendChannel.Close();
+ iRecvChannel.Close();
+
+ iSubModuleNames.Close();
+ iSubModuleAddrs.Close();
+
+ TDblQueIter<CTask> taskIter(iTaskList);
+ CTask *pTask;
+ while ((pTask = taskIter++) != NULL)
+ {
+ delete pTask;
+ }
+ }
+
+TUint CModule::GetNextTransactionId()
+/** Generate a transaction id 'unique' to this module. Uses a straightforward 32 bit
+count so will wrap eventually.
+@return New transaction identifier
+*/
+ {
+ TId iNextTransactionId = iTransactionId.GetId();
+ if(iNextTransactionId==static_cast<TId>(CTask::ENoReplyExpected))
+ {
+ iNextTransactionId = iTransactionId.GetId();
+ }
+ return iNextTransactionId;
+ }
+
+TBool CModule::CheckDiscoveryMsgFinishedL( const TCFMessage& aMsg )
+ {
+ TBool retCode = ETrue;
+ if(aMsg.Code()==TCFCommsMessage::ECodeDiscoverResp)
+ {
+ const TCFDiscoverRespMsg& discoverRespMsg =(reinterpret_cast<const TCFDiscoverRespMsg&>(aMsg));
+
+ if(discoverRespMsg.ReturnCode()>0)
+ {
+ __ASSERT_DEBUG(iDiscoveryArray, User::Panic(KSpecAssert_ElemRootServerbm, 2)); // reassure LINT
+ for(TInt i=0;i<discoverRespMsg.ReturnCode();i++)
+ {
+ iSubModuleNames.Append(iDiscoveryArray[i]);
+ }
+ if(discoverRespMsg.MoreToWrite())
+ {
+ TCFDiscoverMsg discoverMsg(discoverRespMsg.Identifier(), EFalse, KDefaultSubmoduleArraySize, iDiscoveryArray);
+ // put msg on send queue
+ SendL(discoverMsg);
+ retCode = EFalse;
+ }
+ else
+ {
+ delete[] iDiscoveryArray;
+ iDiscoveryArray = NULL;
+ }
+ }
+ }
+ return retCode;
+ }
+