commsfwsupport/commselements/rootserver/bindmgr/bm_module.cpp
changeset 0 dfb7c4ff071f
--- /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;
+	}
+