remotecontrol/remotecontrolfw/server/src/controllersession.cpp
changeset 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remotecontrol/remotecontrolfw/server/src/controllersession.cpp	Wed Oct 13 16:20:29 2010 +0300
@@ -0,0 +1,958 @@
+// Copyright (c) 2010 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:
+//
+
+#include <bluetooth/logger.h>
+#include "activehelper.h"
+#include "bearermanager.h"
+#include "controllersession.h"
+#include "messagequeue.h"
+#include "remconmessage.h"
+#include "remconserver.h"
+#include "server.h"
+#include "utils.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_REMCON_SERVER);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("ctsession");
+#endif
+
+
+CRemConControllerSession* CRemConControllerSession::NewL(CRemConServer& aServer,
+	CBearerManager& aBearerManager,
+	const TClientInfo& aClientInfo,
+	TUint aId)
+	{
+	LOG_STATIC_FUNC;
+	CRemConControllerSession* self = new(ELeave) CRemConControllerSession(aServer, aBearerManager, aId);
+	CleanupStack::PushL(self);
+	self->ConstructL(aClientInfo);
+	CLEANUPSTACK_POP1(self);
+	return self;
+	}
+
+CRemConControllerSession::~CRemConControllerSession()
+	{
+	LOG_FUNC;
+
+	delete iPendingMsgProcessor;
+
+	// we will need to tell the server which bearer this used to be connected to
+	// this enables the server to not inform a bearer that is already connected
+	// that its been connected
+	// Tell the server we've gone away- it may start its shutdown timer.
+	iServer.ControllerClientClosed(*this, iRemoteAddress.BearerUid());
+	iPlayerName.Close();
+	}
+
+CRemConControllerSession::CRemConControllerSession(CRemConServer& aServer, 
+	CBearerManager& aBearerManager,
+	TUint aId)
+	: CRemConSession(aServer, aBearerManager, aId)
+	{
+	LOG_FUNC;
+	}
+
+void CRemConControllerSession::ConstructL(const TClientInfo& aClientInfo)
+	{
+	LOG_FUNC;
+	
+	BaseConstructL(aClientInfo);
+
+	iPendingMsgProcessor = new (ELeave) CActiveHelper(*this);
+	
+	LEAVEIFERRORL(iServer.ControllerClientOpened(*this));
+
+	// Set our pointer into the connection history at the current/'Last' item.
+	// Can't do this til we've told the server we exist
+	iServer.SetConnectionHistoryPointer(Id());
+	}
+
+TBool CRemConControllerSession::SupportedMessage(const CRemConMessage& aMsg) const
+	{
+	LOG_FUNC;
+	LOG1(_L("\taMsg.InterfaceUid() = 0x%08x"), aMsg.InterfaceUid());
+
+	// Return true unless this is a command for an unsupported interface
+	TBool result = !(aMsg.MsgType() == ERemConCommand && !FindInterfaceByUid(aMsg.InterfaceUid()));
+
+	LOG1(_L("result = %d"), result);
+	return result;
+	}
+
+void CRemConControllerSession::SetPlayerType(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Controller clients don't provide the additional parameters are optional,
+	// so we just complete the message with KErrNone.
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConControllerSession::CompleteConnect(const TRemConAddress& aAddr, TInt aError)
+	{
+	LOG_FUNC;
+	LOG2(_L("\taError = %d, aAddr.BearerUid = 0x%08x"), aError, aAddr.BearerUid());
+
+	LOG1(_L("\tiRemoteAddress.BearerUid = 0x%08x"), iRemoteAddress.BearerUid());
+	LOG1(_L("\tiConnectBearerMsg.Handle = %d"), iConnectBearerMsg.Handle());
+
+	if ( iRemoteAddress == aAddr )
+		{
+		if ( iConnectBearerMsg.Handle() )
+			{
+			// We are a session that has an outstanding request on this specific 
+			// connection address.
+			CompleteClient(iConnectBearerMsg, aError);
+			}
+		else 
+			{
+			// Connect bearer message is not valid. 
+			// Check for pending messages.
+			CheckForPendingMsg();
+			}
+		}
+	}
+
+void CRemConControllerSession::CompleteDisconnect(const TRemConAddress& aAddr, TInt aError)
+	{
+	LOG_FUNC;
+	LOG2(_L("\taError = %d, aAddr.BearerUid = 0x%08x"), aError, aAddr.BearerUid());
+
+	LOG1(_L("\tiRemoteAddress.BearerUid = 0x%08x"), iRemoteAddress.BearerUid());
+	LOG1(_L("\tiDisconnectBearerMsg.Handle = %d"), iDisconnectBearerMsg.Handle());
+
+	if ( iRemoteAddress == aAddr )
+		{
+		if ( iDisconnectBearerMsg.Handle() )
+			{
+			// We are a session that has an outstanding request on this specific 
+			// connection address.
+			CompleteClient(iDisconnectBearerMsg, aError);
+			}
+		else 
+			{
+			// Diconnect bearer message is not valid. 
+			// Check for pending messages.
+			CheckForPendingMsg();
+			}
+
+		}
+	}
+
+void CRemConControllerSession::ProcessPendingMsgL()
+	{
+	LOG_FUNC;
+	if (!iPendingMsg.Handle())
+		{
+		// This means that the pending connect or disconnect message,
+		// has been cancelled by the time we got here.
+		// (It was cancelled between two following calls:
+		// iPendingMsgProcessor::Complete and iPendingMsgProcessor::RunL
+		return;
+		}
+
+	ServiceL(iPendingMsg);
+	if (iPendingMsg.Handle())
+		{
+		// This means that the pending msg has not been completed in ServiceL call.
+		// It was stored either in iConnectBearerMsg or iDisconnectBearerMsg member.
+		// This also means that this message is not "pending" any more 
+		// (as processing of its copy has been started). 
+		// However because the copy will get completed we need to 
+		// clean iPendingMsg.iHandle here
+		// To supress coverity error for uninitialized use of 'emptyMsg' coverity annotations
+		// are used as the in-line default constructor of RMessage2 doesn't initialize all member variables.
+		// coverity[var_decl]
+		RMessage2 emptyMsg;
+		iPendingMsg = emptyMsg;
+		}
+	}
+
+void CRemConControllerSession::CheckForPendingMsg() const
+	{
+	LOG_FUNC;
+	if (iPendingMsg.Handle())
+		{
+		ASSERT_DEBUG(iPendingMsgProcessor);
+		iPendingMsgProcessor->Complete();
+		}
+	}
+
+CRemConMessage* CRemConControllerSession::DoPrepareSendMessageL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we don't have a disconnect outstanding- this makes no sense from 
+	// a client viewpoint (they should cancel the disconnect first).
+	// [The client is allowed to have a connect request outstanding- the 
+	// bearer manager makes sure a bearer-level connect is not posted on the 
+	// same address twice.]
+	if ( iDisconnectBearerMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return NULL;
+		}
+
+	// Get the data the client wants to send.
+	TUid interfaceUid;
+	TUint operationId;
+
+	TRemConMessageSubType messageSubType;
+	RBuf8 sendDes;
+	if (!DoGetSendInfoLC(aMessage, interfaceUid, operationId, messageSubType, sendDes))
+		{
+		// DoGetSendInfoLC() panicked the message
+		return NULL;
+		}
+
+	CRemConMessage* msg = NULL;
+	LOG(_L("\tCONTROLLER send"));
+	if (  (messageSubType == ERemConNotifyCommandAwaitingInterim)
+	   || (messageSubType == ERemConNotifyCommandAwaitingChanged)
+		)
+		{
+		LOG(_L("\terror, not allowed to use Send() to send notify command"));
+		CleanupStack::PopAndDestroy(&sendDes);
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicIllegalIpc);
+		}
+	else
+		{
+		msg = CRemConMessage::NewL(
+				iRemoteAddress, // either specified (if we're connection-oriented) or null (we're connectionless- this field will be filled in by the TSP)
+				ERemConCommand, 
+				messageSubType,
+				interfaceUid,
+				operationId,
+				sendDes, // msg takes ownership
+				Id(), // session id for when the response comes back
+				0, // we let the bearer manager invent a new transaction id when the message gets to it
+				ETrue);
+		CLEANUPSTACK_POP1(&sendDes); // now owned by msg
+		}		
+
+	return msg;
+	}
+
+void CRemConControllerSession::SendUnreliable(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	// Check we don't have a disconnect outstanding- this makes no sense from 
+	// a client viewpoint (they should cancel the disconnect first).
+	// [The client is allowed to have a connect request outstanding- the 
+	// bearer manager makes sure a bearer-level connect is not posted on the 
+	// same address twice.]
+	if ( iDisconnectBearerMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+
+	CRemConMessage* msg = NULL;
+	TRAPD(err, msg = DoCreateUnreliableMessageL(aMessage));
+	CompleteClient(aMessage, err);
+	if (err == KErrNone)
+		{
+		ASSERT_DEBUG(iSendQueue);
+		if (iSending || !iSendQueue->IsEmpty())
+			{
+			iSendQueue->Append(*msg);
+			}
+		else
+			{
+			SendToServer(*msg);
+			}
+		}
+	}
+
+CRemConMessage* CRemConControllerSession::DoCreateUnreliableMessageL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Get the data the client wants to send.
+	TUid interfaceUid;
+	TUint operationId;
+	TRemConMessageSubType messageSubType;
+	RBuf8 sendDes;
+	DoGetSendInfoLC(aMessage, interfaceUid, operationId, messageSubType, sendDes);
+
+	// Before we ask the server to send, we must set our ClientInfo 
+	// correctly so the TSP can get information about the client. 
+	iClientInfo.Message() = aMessage;
+
+	CRemConMessage* msg = NULL;
+	
+	LOG(_L("\tCONTROLLER send"));
+	
+	// A client is not allowed to send an unreliable notify command.
+	if	(	(messageSubType == ERemConNotifyCommandAwaitingInterim)
+		||	(messageSubType == ERemConNotifyCommandAwaitingChanged)
+		)
+		{
+		LOG(_L8("\tNot allowed to send unreliable notify command"));
+		CleanupStack::PopAndDestroy(&sendDes);
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicIllegalIpc);
+		LEAVEL(KErrBadDescriptor);
+		}
+	
+	msg = CRemConMessage::NewL(
+		iRemoteAddress, // either specified (if we're connection-oriented) or null (we're connectionless- this field will be filled in by the TSP)
+		ERemConCommand, // controllers can only send commands
+		messageSubType,
+		interfaceUid,
+		operationId,
+		sendDes, // msg takes ownership
+		Id(), // session id for when the response comes back
+		0, // we let the bearer manager invent a new transaction id when the message gets to it
+		EFalse);
+	CLEANUPSTACK_POP1(&sendDes); // now owned by msg
+
+	return msg;
+	}
+
+void CRemConControllerSession::RegisterInterestedAPIs(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+	// No interfaces should have been registered yet!
+	ASSERT_DEBUG(iInterestedAPIs == NULL);
+	
+	TRAPD(err, iInterestedAPIs = ExtractInterestedAPIsL(aMessage));
+	
+	iServer.ControllerClientAvailable();
+
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConControllerSession::GoConnectionOriented(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( !iRemoteAddress.IsNull() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicAlreadyConnectionOriented);
+		return;
+		}
+
+	if ( iConnectBearerMsg.Handle() || iDisconnectBearerMsg.Handle() || iSendMsg.Handle())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+	if (iSending != ENotSending)
+		{
+		DoSendCancel();
+		}
+	EmptySendQueue();
+	
+	// Get the desired address from the message and check it.
+	const TUid uid = TUid::Uid(aMessage.Int0());
+	LOG1(_L("\tuid = 0x%08x"), uid);
+	// Check the requested bearer exists.
+	TBool bearerExists = iBearerManager.BearerExists(uid);
+	if ( !bearerExists)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerPluginIncorrectInterface);
+		return;
+		}
+	// Check the bearer-specific part of the address.
+	TBuf8<TRemConAddress::KMaxAddrSize> buf;
+	TInt err = aMessage.Read(1, buf);
+	if ( err != KErrNone )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+		return;
+		}
+
+	// Do security check- if this client won't be allowed to use the bearer 
+	// then fail the request. 
+	// NB This security check (repeated in debug at ConnectBearer and 
+	// DisconnectBearer time) is all that stands between a connection-oriented 
+	// client and the bearer, and is all the caps checking that RemCon does!
+	err = KErrPermissionDenied;
+	if ( iBearerManager.CheckPolicy(uid, aMessage) )
+		{
+		err = KErrNone;
+		}
+		
+		
+	// if alls well and we're connection oriented then set up as such
+	if (KErrNone == err)
+		{
+		// The client has passed all our checks- set our data member.
+		iRemoteAddress.BearerUid() = uid;
+		iRemoteAddress.Addr() = buf;
+		// tell the server
+		iServer.ClientGoConnectionOriented(*this,uid);
+		}
+				
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConControllerSession::GoConnectionless(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( iRemoteAddress.IsNull() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicNotConnectionOriented);
+		return;
+		}
+
+	if ( iConnectBearerMsg.Handle() || iDisconnectBearerMsg.Handle() || iSendMsg.Handle())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+	
+	if (iSending != ENotSending)
+		{
+		DoSendCancel();
+		}
+	EmptySendQueue();
+	
+	// we will need to tell the server which bearer this used to be connected to
+	// this enables the server to not inform a bearer that is already connected
+	// that its been connected
+	TUid oldUid = iRemoteAddress.BearerUid();
+	
+	iRemoteAddress.BearerUid() = KNullUid;	
+
+	// tell the server
+	iServer.ClientGoConnectionless(*this, oldUid);
+
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConControllerSession::ConnectBearer(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( iConnectBearerMsg.Handle() || iDisconnectBearerMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+
+	if ( iRemoteAddress.IsNull() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicNotConnectionOriented);
+		return;
+		}
+
+	// Check the requested bearer exists.
+	TBool bearerExists = iBearerManager.BearerExists(iRemoteAddress.BearerUid());
+	// This check was done at GoConnectionOriented time.
+	ASSERT_DEBUG(bearerExists);
+	// So was this one.
+	ASSERT_DEBUG(iBearerManager.CheckPolicy(iRemoteAddress.BearerUid(), aMessage));
+
+	// Check the state of our given connection at the bearer level. If it is: 
+	// -) disconnected request the connection to come up,
+	// -) connecting or disconnecting, add message to the queue of pending 
+	//		messages, and process it once connecting/disconnecting has been completed
+	// -) connected, complete the client's message,
+
+	TConnectionState conState;
+	conState = iServer.ConnectionState(iRemoteAddress);
+
+	if ( conState == EDisconnected )
+		{
+		// The bearer may indicate connection synchronously, so set this 
+		// message _before_ we ask them
+		iConnectBearerMsg = aMessage;
+		TInt err = iBearerManager.Connect(iRemoteAddress);
+		if ( err != KErrNone )
+			{
+			CompleteClient(iConnectBearerMsg, err);
+			}
+		}
+	else if ( conState == EDisconnecting ||  conState == EConnecting )
+		{
+		if ( iPendingMsg.Handle() )
+			{
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+			return;
+			}
+		// Store the message, it will get processed later.
+		iPendingMsg = aMessage;
+		}
+	else // EConnected
+		{
+		CompleteClient(aMessage, KErrNone);
+		}
+		
+	}
+
+void CRemConControllerSession::ConnectBearerCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( iConnectBearerMsg.Handle() )
+		{
+		CompleteClient(iConnectBearerMsg, KErrCancel);
+		}
+	else if ( iPendingMsg.Handle() && ( iPendingMsg.Function() == ERemConConnectBearer ))
+		{
+		CompleteClient(iPendingMsg, KErrCancel);
+		}
+		
+	CompleteClient(aMessage, KErrNone);
+	// At no point do we make any change to the processes going on underneath
+	// us- 'Cancel' APIs are just for cancelling interest in an async
+	// operation.
+	}
+
+void CRemConControllerSession::DisconnectBearer(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( iDisconnectBearerMsg.Handle() || iConnectBearerMsg.Handle() || iSendMsg.Handle())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+
+	if ( iRemoteAddress.IsNull() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicNotConnectionOriented);
+		return;
+		}
+
+	if (iSending != ENotSending)
+		{
+		DoSendCancel();
+		}
+	EmptySendQueue();
+	
+	// Check the requested bearer exists.
+	TBool bearerExists = iBearerManager.BearerExists(iRemoteAddress.BearerUid());
+	// This check was done at GoConnectionOriented time.
+	ASSERT_DEBUG(bearerExists);
+	// So was this one.
+	ASSERT_DEBUG(iBearerManager.CheckPolicy(iRemoteAddress.BearerUid(), aMessage));
+
+	// Check the state of the given connection. If it is:
+	// -) connected, request connection to go away,
+	// -) disconnected, compete the client's message,
+	// -) connecting or disconnecting, add message to the queue of pending 
+	//		messages, and process it once connecting/disconnecting has been completed
+
+	TInt err;
+	TConnectionState conState;
+	conState = iServer.ConnectionState(iRemoteAddress);
+
+	if ( conState == EConnected )
+		{
+		// The bearer may indicate disconnection synchronously, so set this 
+		// message _before_ we ask them
+		iDisconnectBearerMsg = aMessage;
+		err = iBearerManager.Disconnect(iRemoteAddress);
+		if ( err != KErrNone )
+			{
+			CompleteClient(iDisconnectBearerMsg, err);
+			}
+		}
+	else if ( conState == EDisconnecting ||  conState == EConnecting )
+		{
+		if ( iPendingMsg.Handle() )
+			{
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+			return;
+			}
+		// Store the message, it will get processed later.
+		iPendingMsg = aMessage;
+		}
+	else //disconnected
+		{
+		CompleteClient(aMessage, KErrNone);	
+		}
+	}
+
+void CRemConControllerSession::DisconnectBearerCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	if ( iDisconnectBearerMsg.Handle() )
+		{
+		CompleteClient(iDisconnectBearerMsg, KErrCancel);
+		}
+	else if ( iPendingMsg.Handle() && (iPendingMsg.Function() == ERemConDisconnectBearer ))
+		{
+		CompleteClient(iPendingMsg, KErrCancel);
+		}
+		
+	CompleteClient(aMessage, KErrNone);
+	}
+
+/**
+Sends a notify message to the remote device.
+
+This function is intended for the RemCon controller client to send a notify
+command to the remote device.
+*/
+void CRemConControllerSession::SendNotify(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we're not already sending...
+	if ( iSendMsg.Handle())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicSendAlreadyOutstanding);
+		return;
+		}
+	
+	iSendMsg = aMessage;
+	
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	// Check we don't have a disconnect outstanding- this makes no sense from 
+	// a client viewpoint (they should cancel the disconnect first).
+	// [The client is allowed to have a connect request outstanding- the 
+	// bearer manager makes sure a bearer-level connect is not posted on the 
+	// same address twice.]
+	if ( iDisconnectBearerMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBearerControlOutstanding);
+		return;
+		}
+
+	TRAPD(err, DoSendNotifyL(aMessage));
+	if ( err != KErrNone )
+		{
+		CompleteClient(aMessage, err);
+		}
+	}
+
+/**
+@see CRemConControllerSession::SendNotify
+*/
+void CRemConControllerSession::DoSendNotifyL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Get the data the client wants to send.
+	const TUid interfaceUid = TUid::Uid(aMessage.Int0());
+	LOG1(_L("\tinterfaceUid = 0x%08x"), interfaceUid);
+
+	if (aMessage.GetDesLengthL(1) != sizeof(TOperationInformation))
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+		return;
+		}
+
+	TPckgBuf<TOperationInformation> opInfoPckg;	
+	TInt err= aMessage.Read(
+			1, // location of the descriptor in the client's message (as we expect them to have set it up)
+			opInfoPckg, // descriptor to write to from client memory space
+			0 // offset into our descriptor to put the client's data
+			);
+	
+	if ( err != KErrNone )
+		{
+		LOG1(_L("\taMessage.Read = %d"), err);
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+		return;
+		}	
+	
+	const TUint operationId = opInfoPckg().iOperationId;
+	LOG1(_L("\toperationId = 0x%02x"), operationId);
+	
+	const TRemConMessageSubType messageSubType = opInfoPckg().iMessageSubType;
+	LOG1(_L("\tmessageSubType = 0x%02x"), messageSubType);
+	
+	const TUint dataLength = (TUint)aMessage.GetDesLengthL(2);
+	LOG1(_L("\tdataLength = %d"), dataLength);
+	
+	// If the client wanted to send some operation-associated data, read it 
+	// from them.
+	RBuf8 sendDes;
+	if ( dataLength != 0 )
+		{
+		sendDes.CreateL(dataLength);
+		TInt err = aMessage.Read(
+			2, // location of the descriptor in the client's message (as we expect them to have set it up)
+			sendDes, // descriptor to write to from client memory space
+			0 // offset into our descriptor to put the client's data
+			);
+		// NB We don't do LEAVEIFERRORL(aMessage.Read) because a bad client 
+		// descriptor is a panicking offence for them, not an 'error the 
+		// request' offence.
+		if ( err != KErrNone )
+			{
+			LOG1(_L("\taMessage.Read = %d"), err);
+			sendDes.Close();
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+			return;
+			}
+		}
+
+	// Before we ask the server to send, we must set our ClientInfo 
+	// correctly so the TSP can get information about the client. 
+	iClientInfo.Message() = aMessage;
+
+	CRemConMessage* msg = NULL;
+	
+	if (messageSubType != ERemConNotifyCommandAwaitingInterim)
+		{
+		sendDes.Close();
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicIllegalIpc);
+		return;
+		}
+	
+	CleanupClosePushL(sendDes);
+	msg = CRemConMessage::NewL(
+			iRemoteAddress, // either specified (if we're connection-oriented) or null (we're connectionless- this field will be filled in by the TSP)
+			ERemConNotifyCommand, 
+			messageSubType,
+			interfaceUid,
+			operationId,
+			sendDes, // msg takes ownership
+			Id(), // session id for when the response comes back
+			0, // we let the bearer manager invent a new transaction id when the message gets to it
+			ETrue);	
+	CLEANUPSTACK_POP1(&sendDes); // now owned by msg
+	
+	LOG(_L("\tCONTROLLER send"));
+	ASSERT_DEBUG(iSendQueue);
+	if (iSending != ENotSending || !iSendQueue->IsEmpty())
+		{
+		iSendQueue->Append(*msg);
+		}
+	else
+		{
+		SendToServer(*msg);
+		}
+	}
+
+void CRemConControllerSession::SendToServer(CRemConMessage& aMsg)
+	{
+	LOG_FUNC;
+	
+	// Set our completion members.
+	NumRemotes() = 0;
+	NumRemotesToTry() = 0;
+	SendError() = KErrNone;
+
+	
+	iSending = (aMsg.IsReliableSend()) ? ESendingReliable: ESendingUnreliable;
+	
+	iServer.SendCommand(aMsg);
+	}
+
+void CRemConControllerSession::EmptySendQueue()
+	{
+	LOG_FUNC;
+
+	ASSERT_DEBUG(!iSendMsg.Handle())
+	ASSERT_DEBUG(iSendNextCallBack);
+	iSendNextCallBack->Cancel();
+	CRemConMessage* msg;
+	ASSERT_DEBUG(iSendQueue);
+	TSglQueIter<CRemConMessage>& iter = iSendQueue->SetToFirst();
+	while ((msg = iter++) != NULL)
+		{
+		iSendQueue->RemoveAndDestroy(*msg);
+		}
+	}
+
+void CRemConControllerSession::DoSendCancel()
+	{
+	LOG_FUNC;
+	// We must tell the server, and pull the CRemConMessage from the 
+	// 'outgoing pending TSP' queue if it's on it. If the TSP is currently 
+	// processing the CRemConMessage, we must tell it to stop before we 
+	// can complete the RMessage2 iSendMsg- the TSP might still be 
+	// dereferencing bits of it. (The TSP is given iSendMsg so it can 
+	// access the client's secure ID and do a capability check.)
+	// NB This only matters for commands- responses don't go through the 
+	// TSP.
+	// Not also that this processing *stops* this 
+	// CRemConSession::SendCancel method from being the very simple 'I'm 
+	// no longer interested in the completion of the asynchronous request' 
+	// type of API it (and all cancels) should be. It actually does work 
+	// as well. As long as this work is implemented _synchronously_, we 
+	// should be OK.
+	iServer.SendCancel(*this);
+
+	NumRemotesToTry() = 0;
+	iSendError = KErrCancel;
+	CompleteSend();
+	}
+
+void CRemConControllerSession::CompleteMessage(const CRemConMessage& aMessage)
+	{
+	LOG_FUNC;
+
+	switch (aMessage.MsgType())
+		{
+	case ERemConCommand:
+	case ERemConResponse:
+	case ERemConReject:
+		{
+		CompleteSend();
+		break;
+		}
+	case ERemConNotifyCommand:
+		{
+		CompleteSendNotify();
+		break;
+		}
+	default:
+		ASSERT_DEBUG(EFalse);
+		break;
+		}
+
+	}
+
+void CRemConControllerSession::DoReceive()
+	{
+	// Request messages from the server for this controller session.
+	// If there's anything waiting to be given to us, ReceiveRequest will call 
+	// back to us with it.
+	iServer.ReceiveRequest(*this);	
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendResult(const CRemConMessage& aMessage, TInt aError)
+	{
+	LOG_FUNC;
+
+	// We should not already be sending a message to n remotes
+	ASSERT_DEBUG(NumRemotesToTry() == 0);
+
+	SendError() = aError;
+	CompleteMessage(aMessage);
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendOneOrMoreAttempt(const CRemConMessage& /*aMessage*/, TUint aNumRemotes)
+	{
+	LOG_FUNC;
+
+	// We should not already be sending a message
+	ASSERT_DEBUG(NumRemotesToTry() == 0);
+
+	NumRemotes() = 0;
+	NumRemotesToTry() = aNumRemotes;
+	SendError() = KErrNone;
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendOneOrMoreIncremental(const CRemConMessage& /*aMessage*/, TUint /*aNumRemotes*/)
+	{
+	LOG_FUNC;
+
+	// This method should never be called, as it is not required to support controller sessions.
+	ASSERT_DEBUG(EFalse);
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendOneOrMoreAttemptFailed(const CRemConMessage& aMessage, TInt aError)
+	{
+	LOG_FUNC;
+
+	// We should not already be sending a message
+	ASSERT_DEBUG(NumRemotesToTry() == 0);
+
+	NumRemotes() = 0;
+	SendError() = aError;
+	CompleteMessage(aMessage);
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendOneOrMoreResult(const CRemConMessage& aMessage, TInt aError)
+	{
+	LOG_FUNC;
+
+	// Ignore notification if client has been completed
+	if (NumRemotesToTry() > 0)
+		{
+		// Only set error if different from KErrNone
+		if (aError == KErrNone)
+			{
+			++NumRemotes();
+			}
+		else
+			{
+			SendError() = aError;
+			}
+
+		--NumRemotesToTry();
+		if (NumRemotesToTry() == 0)
+			{
+			CompleteMessage(aMessage);
+			}
+		}
+	}
+
+void CRemConControllerSession::MrcmsoMessageSendOneOrMoreAbandoned(const CRemConMessage& /*aMessage*/)
+	{
+	LOG_FUNC;
+
+	// This method should never be called, as it is not required to support controller sessions.
+	ASSERT_DEBUG(EFalse);
+	}