accessoryservices/remotecontrolfw/server/src/session.cpp
changeset 0 4e1aa6a622a0
child 56 1ddbe54d0645
child 70 653a8b91b95e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/accessoryservices/remotecontrolfw/server/src/session.cpp	Tue Feb 02 00:53:00 2010 +0200
@@ -0,0 +1,1814 @@
+// Copyright (c) 2004-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:
+// Remote Control session implementation.
+// 
+//
+
+/**
+ @file
+ @internalComponent
+*/
+#include "e32base.h"
+
+#include <bluetooth/logger.h>
+#include <remcon/remconbearerinterface.h>
+#include <remcon/remconifdetails.h>
+#include <s32mem.h>
+#include "utils.h"
+#include "server.h"
+#include "bearermanager.h"
+#include "remconmessage.h"
+#include "connections.h"
+#include "activehelper.h"
+#include "session.h"
+#include "messagequeue.h"
+#include "remconserver.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_REMCON_SERVER);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("session");
+#endif
+
+CRemConSession* CRemConSession::NewL(CRemConServer& aServer,
+		CBearerManager& aBearerManager,
+		const RMessage2& aMessage,
+		TUint aId)
+	{
+	LOG_STATIC_FUNC
+	CRemConSession* self = new(ELeave) CRemConSession(aServer, aBearerManager, aId);
+	CleanupStack::PushL(self);
+	self->ConstructL(aMessage);
+	CLEANUPSTACK_POP1(self);
+	return self;
+	}
+
+CRemConSession::CRemConSession(CRemConServer& aServer,
+		CBearerManager& aBearerManager,
+		TUint aId)
+ :	iServer(aServer),
+	iBearerManager(aBearerManager),
+	iId(aId)
+	{
+	LOG_FUNC
+	}
+
+void CRemConSession::ConstructL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Get the client's process ID.
+	RThread thread;
+	LEAVEIFERRORL(aMessage.Client(thread));
+	CleanupClosePushL(thread);
+	RProcess process;
+	LEAVEIFERRORL(thread.Process(process));
+	iClientInfo.ProcessId() = process.Id();
+	process.Close();
+	iClientInfo.SecureId() = thread.SecureId();
+	CleanupStack::PopAndDestroy(&thread);
+
+	iSendQueue = CMessageQueue::NewL();
+	
+	TCallBack cb(SendNextCb, this);
+	
+	iSendNextCallBack = new(ELeave) CAsyncCallBack(cb, CActive::EPriorityStandard);
+	
+	// Tell the server about us.
+	LEAVEIFERRORL(iServer.ClientOpened(*this));
+
+	// Set our pointer into the connection history at the current/'Last' item.
+	iServer.SetConnectionHistoryPointer(Id());
+	
+	iPendingMsgProcessor = new (ELeave) CActiveHelper(*this);
+	}
+
+CRemConSession::~CRemConSession()
+	{
+	LOG(KNullDesC8);
+	LOG_FUNC;
+	
+	delete iPendingMsgProcessor;
+	delete iSendNextCallBack;
+	delete iSendQueue;
+	// 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.ClientClosed(*this, iRemoteAddress.BearerUid());
+	delete iInterestedAPIs;
+	iPlayerName.Close();
+	}
+
+void CRemConSession::ServiceL(const RMessage2& aMessage)
+	{
+	LOG(KNullDesC8);
+	LOG_FUNC;
+	LOG1(_L("\taMessage.Function() = %d"), aMessage.Function());
+
+	// Switch on the IPC number and call a 'message handler'. Message handlers 
+	// complete aMessage (either with Complete or Panic), or make a note of 
+	// the message for later asynchronous completion.
+	// Message handlers should not leave- the server does not have an Error 
+	// function. 
+
+	switch ( aMessage.Function() )
+		{
+	// Heap failure testing APIs.
+	case ERemConDbgMarkHeap:
+#ifdef _DEBUG
+		LOG(_L("\tmark heap"));
+		__UHEAP_MARK;
+#endif // _DEBUG
+		CompleteClient(aMessage, KErrNone);
+		break;
+
+	case ERemConDbgCheckHeap:
+#ifdef _DEBUG
+		LOG1(_L("\tcheck heap (expecting %d cells)"), aMessage.Int0());
+		__UHEAP_CHECK(aMessage.Int0());
+#endif // _DEBUG
+		CompleteClient(aMessage, KErrNone);
+		break;
+
+	case ERemConDbgMarkEnd:
+#ifdef _DEBUG
+		LOG1(_L("\tmark end (expecting %d cells)"), aMessage.Int0());
+		__UHEAP_MARKENDC(aMessage.Int0());
+#endif // _DEBUG
+		CompleteClient(aMessage, KErrNone);
+		break;
+
+	case ERemConDbgFailNext:
+#ifdef _DEBUG
+		{
+		LOG1(_L("\tfail next (simulating failure after %d allocation(s))"), aMessage.Int0());
+		if ( aMessage.Int0() == 0 )
+			{
+			__UHEAP_RESET;
+			}
+		else
+			{
+			__UHEAP_FAILNEXT(aMessage.Int0());
+			}
+		}
+#endif // _DEBUG
+		CompleteClient(aMessage, KErrNone);
+		break;
+
+	case ERemConSetClientType:
+		SetClientType(aMessage);
+		// This is a sync API- check that the message has been completed.
+		// (NB We don't check the converse for async APIs because the message 
+		// may have been panicked synchronously.)
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConGoConnectionOriented:
+		GoConnectionOriented(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConGoConnectionless:
+		GoConnectionless(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConConnectBearer:
+		ConnectBearer(aMessage);
+		break;
+
+	case ERemConConnectBearerCancel:
+		ConnectBearerCancel(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConDisconnectBearer:
+		DisconnectBearer(aMessage);
+		break;
+
+	case ERemConDisconnectBearerCancel:
+		DisconnectBearerCancel(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConSend:
+		Send(aMessage);
+		break;
+		
+	case ERemConSendNotify:
+		SendNotify(aMessage);
+		break;
+		
+	case ERemConSendUnreliable:
+		SendUnreliable(aMessage);
+		break;
+	
+	case ERemConSendCancel:
+		SendCancel(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConReceive:
+		Receive(aMessage);
+		break;
+
+	case ERemConReceiveCancel:
+		ReceiveCancel(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConGetConnectionCount:
+		GetConnectionCount(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConGetConnections:
+		GetConnections(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+
+	case ERemConNotifyConnectionsChange:
+		NotifyConnectionsChange(aMessage);
+		break;
+
+	case ERemConNotifyConnectionsChangeCancel:
+		NotifyConnectionsChangeCancel(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+		
+	case ERemConRegisterInterestedAPIs:
+		RegisterInterestedAPIs(aMessage);
+		ASSERT_DEBUG(aMessage.IsNull());
+		break;
+		
+	default:
+		// Unknown message
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicIllegalIpc);
+		break;
+		}
+	}
+
+void CRemConSession::CompleteClient(const RMessage2& aMessage, TInt aError)
+	{
+	LOG1(_L("\tcompleting client message with %d"), aError);
+	TBool cleanClientInfoMessage = (iClientInfo.Message().Handle() == aMessage.Handle());
+	aMessage.Complete(aError);
+	if(cleanClientInfoMessage)
+		{
+		iClientInfo.Message() = RMessage2();
+		}
+	}
+
+void CRemConSession::SetClientType(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	if ( iType != ERemConClientTypeUndefined )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeAlreadySet);
+		return;
+		}
+
+	const TRemConClientType type = static_cast<TRemConClientType>(aMessage.Int0());
+	LOG1(_L("\trequested (TRemConClientType) type = %d"), type);
+	
+	TInt err = aMessage.GetDesLength(1);
+	if(err >= 0)
+		{
+		TRAP(err, DoSetClientTypeL(aMessage));
+		if(err == KErrBadDescriptor)
+			{
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+			return;
+			}
+		}
+	else if(err == KErrBadDescriptor)
+		{
+		// The additional parameters are optional (i.e. old or controller clients won't provide them).
+		err = KErrNone;
+		}
+	
+	if(err != KErrNone)
+		{
+		CompleteClient(aMessage, err);
+		}
+	else
+		{
+		switch ( type )
+			{
+		case ERemConClientTypeController:
+			iType = type;
+			CompleteClient(aMessage, KErrNone);
+			break;
+	
+		case ERemConClientTypeTarget:
+			// Check that there aren't already any target clients with the
+			// same process ID.
+			if ( !iServer.TargetClientWithSameProcessId(iClientInfo.ProcessId()) )
+				{
+				iType = type;
+				CompleteClient(aMessage, KErrNone);
+				}
+			else
+				{
+				CompleteClient(aMessage, KErrInUse);
+				}
+			break;
+	
+		default:
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+			break;
+			}
+		}
+
+	if ( iType != ERemConClientTypeUndefined )
+		{
+		// The type got set, so tell the server, so it can tell the bearers if 
+		// necessary. 
+		iServer.ClientTypeSet(*this);
+		}
+	}
+
+void CRemConSession::DoSetClientTypeL(const RMessage2& aMessage)
+	{
+	// Retrieve the client type information 
+	RBuf8 typeBuf;
+	typeBuf.CreateL(sizeof(TPlayerTypeInformation));
+	CleanupClosePushL(typeBuf);
+	aMessage.ReadL(1, typeBuf);
+			
+	const TPlayerTypeInformation* Ptr = reinterpret_cast<const TPlayerTypeInformation*> (typeBuf.Ptr());
+	iPlayerType.iPlayerType = (*Ptr).iPlayerType;
+	iPlayerType.iPlayerSubType = (*Ptr).iPlayerSubType;
+	// Retrieve the client player name inforamtion
+	iPlayerName.CreateL(aMessage.Int2());
+	CleanupClosePushL(iPlayerName);
+	aMessage.ReadL(3, iPlayerName);	
+	CleanupStack::Pop(&iPlayerName);
+	
+	CleanupStack::PopAndDestroy(&typeBuf); 
+	}
+void CRemConSession::GoConnectionOriented(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		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 CRemConSession::GoConnectionless(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		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 CRemConSession::ConnectBearer(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		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 CRemConSession::ConnectBearerCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		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 CRemConSession::DisconnectBearer(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		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 CRemConSession::DisconnectBearerCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if ( iType != ERemConClientTypeController )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+		return;
+		}
+
+	if ( iDisconnectBearerMsg.Handle() )
+		{
+		CompleteClient(iDisconnectBearerMsg, KErrCancel);
+		}
+	else if ( iPendingMsg.Handle() && (iPendingMsg.Function() == ERemConDisconnectBearer ))
+		{
+		CompleteClient(iPendingMsg, KErrCancel);
+		}
+		
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConSession::Send(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 type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		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, DoSendL(aMessage));
+	if ( err != KErrNone )
+		{
+		CompleteClient(aMessage, err);
+		}
+	}
+
+/**
+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 CRemConSession::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 type set...
+	if (Type() != ERemConClientTypeController)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		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);
+		}
+	}
+
+void CRemConSession::DoSendL(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(3);
+	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(
+			3, // 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;
+			}
+		}
+	CleanupClosePushL(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;
+	
+	// Examine the session type.
+	switch ( iType )
+		{
+	case ERemConClientTypeTarget:
+		{
+		LOG(_L("\tTARGET send"));
+
+		msg = CRemConMessage::NewL(
+			TRemConAddress(), // we don't know which remotes it's going to yet
+			ERemConResponse, // targets can only send responses
+			messageSubType,
+			interfaceUid,
+			operationId,
+			sendDes, // msg takes ownership
+			Id(), // session id to match this response against the originating command
+			0, // transaction id not yet known
+			ETrue);
+		CLEANUPSTACK_POP1(&sendDes); // now owned by msg
+		
+		}
+		break;
+
+	case ERemConClientTypeController:
+		{
+		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);
+        	return;
+        	}
+        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
+
+		}
+		break;
+
+	default:
+		DEBUG_PANIC_LINENUM;
+		break;
+		}
+	
+	ASSERT_DEBUG(iSendQueue);
+	// We know msg is valid at this stage as the code would leave or panic earlier if msg was 
+	// not set.
+	ASSERT_DEBUG(msg);
+	
+	if (iSending != ENotSending || !iSendQueue->IsEmpty())
+		{
+		iSendQueue->Append(*msg);
+		}
+	else
+		{
+		// we know msg cannot be null here as said above.
+		SendToServer(*msg);
+		}
+	}
+
+/**
+@see CRemConSession::SendNotify
+*/
+void CRemConSession::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 CRemConSession::SendUnreliable(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		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* CRemConSession::DoCreateUnreliableMessageL(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);
+		LEAVEL(KErrBadDescriptor);
+		}
+
+	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);
+		LEAVEL(KErrBadDescriptor);
+		}	
+	
+	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);
+			LEAVEL(KErrBadDescriptor);
+			}
+		}
+	CleanupClosePushL(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;
+	
+	// Examine the session type.
+	switch ( iType )
+		{
+	case ERemConClientTypeTarget:
+		{
+		LOG(_L("\tTARGET send"));
+
+		msg = CRemConMessage::NewL(
+			TRemConAddress(), // we don't know which remotes it's going to yet
+			ERemConResponse, // targets can only send responses
+			messageSubType,
+			interfaceUid,
+			operationId,
+			sendDes, // msg takes ownership
+			Id(), // session id to match this response against the originating command
+			0, // transaction id not yet known
+			EFalse);
+		CLEANUPSTACK_POP1(&sendDes); // now owned by msg
+		break;
+		}
+
+	case ERemConClientTypeController:
+		{
+		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
+		
+		}
+		break;
+
+	default:
+		DEBUG_PANIC_LINENUM;
+		break;
+		}
+
+	return msg;
+	}
+
+void CRemConSession::SendToServer(CRemConMessage& aMsg)
+	{
+	LOG_FUNC;
+	
+	// Set our completion members.
+	NumRemotes() = 0;
+	NumRemotesToTry() = 0;
+	SendError() = KErrNone;
+
+	
+	iSending = (aMsg.IsReliableSend()) ? ESendingReliable: ESendingUnreliable;
+	switch ( iType )
+		{
+	case ERemConClientTypeTarget:
+		{
+		iServer.SendResponse(aMsg, *this);
+		break;
+		}
+	case ERemConClientTypeController:
+		{
+		iServer.SendCommand(aMsg);
+		break;
+		}
+	default:
+		DEBUG_PANIC_LINENUM;
+		break;
+		}
+	}
+
+
+void CRemConSession::SendCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	// See comments in ConnectBearerCancel.
+	if ( iSendMsg.Handle() )
+		{
+		DoSendCancel();
+		}
+	
+	if (iSendMsg.Handle())
+		{
+		CRemConMessage* msg;
+		TBool first = ETrue;
+		ASSERT_DEBUG(iSendQueue);
+		TSglQueIter<CRemConMessage>& iter = iSendQueue->SetToFirst();
+		while ((msg = iter++) != NULL)
+			{
+			if (msg->IsReliableSend())
+				{
+				CompleteClient(iSendMsg, KErrCancel);
+				iSendQueue->RemoveAndDestroy(*msg);
+				if (first)
+					{
+					ASSERT_DEBUG(iSendNextCallBack);
+					iSendNextCallBack->Cancel();
+					}
+				break;
+				}
+			first = EFalse;
+			}
+		}
+	
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConSession::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 CRemConSession::EmptySendQueue()
+	{
+	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 CRemConSession::Receive(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Messages are pushed from bearers, so we 
+	// (a) do some sanity checking, 
+	// (b) check the queue of incoming messages in case there's anything 
+	// already waiting to be given to the client.
+
+	if ( iReceiveMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicReceiveAlreadyOutstanding);
+		return;
+		}
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	iReceiveMsg = aMessage;
+	// If there's anything waiting to be given to us, ReceiveRequest will call 
+	// back to us with it.
+	iServer.ReceiveRequest(*this);
+	}
+
+void CRemConSession::ReceiveCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	// See comments in ConnectBearerCancel.
+	if ( iReceiveMsg.Handle() )
+		{
+		CompleteClient(iReceiveMsg, KErrCancel);
+		}
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConSession::GetConnectionCount(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	// Get the answer to the question- the number of connections at the 
+	// current point in time (i.e. the latest entry in the connection 
+	// history).
+	const TUint connCount = iServer.Connections().Count();
+	LOG1(_L("\tconnCount = %d"), connCount);
+	TPckg<TUint> count(connCount);
+	TInt err = aMessage.Write(0, count);
+
+	// If the client was told the answer with no error, then remember the 
+	// current point in the connection history, so that when the client asks 
+	// for the connections themselves, we give them a consistent answer.
+	if ( err == KErrNone )
+		{
+		iServer.SetConnectionHistoryPointer(Id());
+		}
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConSession::GetConnections(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	// Get the array of connections at the point in the history we're 
+	// interested in and write it back to the client. NB This is not 
+	// necessarily the Last item in the history but the item that we were 
+	// pointing at when GetConnectionCount was called.
+	const CConnections& conns = iServer.Connections(iId);
+	const TUint count = conns.Count();
+	LOG1(_L("\tcount = %d"), count);
+	RBuf8 buf;
+	TInt err = buf.Create(count * sizeof(TRemConAddress));
+	if ( err == KErrNone )
+		{
+		TSglQueIter<TRemConAddress>& iter = conns.SetToFirst();
+		TRemConAddress* addr;
+		while ( ( addr = iter++ ) != NULL )
+			{
+			buf.Append((TUint8*)addr, sizeof(TRemConAddress));
+			}
+
+		// Write back to the client...
+		err = aMessage.Write(0, buf);
+		buf.Close();
+		if ( err != KErrNone )
+			{
+			// We don't need to call SetConnectionHistoryPointer here because 
+			// the server will do it when it cleans up the panicked client.
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+			return;
+			}	   		
+		}
+
+	// Whether or not there was an error, we're no longer interested in the 
+	// history item we're currently registered as being interested in, so tell 
+	// the server to bump up our pointer to the current (latest) one. NB This 
+	// may in fact be the same record, if no connection changes have occurred 
+	// since GetConnectionCount was called, but it's still important to give 
+	// the server a chance to remove obsolete history records.
+	iServer.SetConnectionHistoryPointer(Id());
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConSession::NotifyConnectionsChange(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Messages are pushed to us from bearers, so we don't need anything more 
+	// than some sanity checking here.
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	if ( iNotifyConnectionsChangeMsg.Handle() )
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicConnectionsNotificationAlreadyOutstanding);
+		}
+	else
+		{
+		iNotifyConnectionsChangeMsg = aMessage;
+		// Check the connection history for any more recent items than that we 
+		// currently know about. If our pointer into the connection history 
+		// isn't pointing at the 'current' item, we can complete the 
+		// notification immediately and move the pointer up.
+		if ( !iServer.ConnectionHistoryPointerAtLatest(Id()) )
+			{
+			CompleteClient(iNotifyConnectionsChangeMsg, KErrNone);
+			iServer.SetConnectionHistoryPointer(Id());
+			}
+		}
+	}
+
+void CRemConSession::NotifyConnectionsChangeCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our type set...
+	if (	Type() != ERemConClientTypeController
+		&&	Type() != ERemConClientTypeTarget
+		)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientTypeNotSet);
+		return;
+		}
+
+	// See comments in ConnectBearerCancel.
+	if ( iNotifyConnectionsChangeMsg.Handle() )
+		{
+		CompleteClient(iNotifyConnectionsChangeMsg, KErrCancel);
+		}
+	CompleteClient(aMessage, KErrNone);
+	}
+
+void CRemConSession::RegisterInterestedAPIs(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+	
+	if(iType == ERemConClientTypeUndefined)
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicRegisterInterestedAPIsInNonTargetSession);
+		}
+	
+	TRAPD(err, DoRegisterInterestedAPIsL(aMessage));
+	
+	if(iType == ERemConClientTypeController)
+		{
+		iServer.ControllerClientAvailable();
+		}
+	else if(err == KErrNone) // must be target
+		{
+		iServer.TargetClientAvailable(*this);
+		
+		TInt count = iInterestedAPIs->Array().Count();
+		for(TInt ix=0; ix<count; ++ix)
+			{
+			CRemConInterfaceDetails* details = iInterestedAPIs->Array()[ix];
+			ASSERT_DEBUG(details);
+			if(details->IsBulk())
+				{
+				iServer.BulkServerRequired();
+				break;
+				}
+			}
+		}
+	
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConSession::DoRegisterInterestedAPIsL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+	
+	RBuf8 buf;
+	buf.CreateL(aMessage.GetDesLengthL(0));
+	CleanupClosePushL(buf);
+	
+	aMessage.ReadL(0, buf);
+	RDesReadStream ipcStream(buf);
+	
+	iInterestedAPIs = CRemConInterfaceDetailsArray::InternalizeL(ipcStream);
+	
+	ipcStream.Close(); 
+	CleanupStack::PopAndDestroy(&buf); 
+	}
+
+void CRemConSession::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 CRemConSession::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 CRemConSession::ConnectionsChanged()
+	{
+	LOG_FUNC;
+
+	LOG1(_L("\tiNotifyConnectionsChangeMsg.Handle = %d"), iNotifyConnectionsChangeMsg.Handle());
+	if ( iNotifyConnectionsChangeMsg.Handle() )
+		{
+		// Set the connection history pointer to point to the latest item and then complete the 
+		// NotifyConnectionChange request of the client.
+		iServer.SetConnectionHistoryPointer(Id());
+		CompleteClient(iNotifyConnectionsChangeMsg, KErrNone);
+		}
+	}
+
+void CRemConSession::CompleteSend()
+	{
+	LOG_FUNC;
+	LOG2(_L("\tiNumRemotes = %d, iSendError = %d"), iNumRemotes, iSendError);
+
+	ASSERT_DEBUG(NumRemotesToTry() == 0);
+	NumRemotesToTry() = -1;
+
+	
+	if (iSending == ESendingReliable)
+		{
+		if ( iSendError == KErrNone )
+			{
+			TPckg<TUint> count(iNumRemotes);
+			// 2 is the slot in the client's message for the number of remotes the 
+			// message got sent to.
+			iSendError = iSendMsg.Write(2, count);
+			}
+		CompleteClient(iSendMsg, iSendError);
+		}
+	
+	ASSERT_DEBUG(iSendQueue);
+	if (!iSendQueue->IsEmpty())
+		{
+		ASSERT_DEBUG(iSendNextCallBack);
+		iSendNextCallBack->CallBack();
+		}
+	iSending = ENotSending;
+	}
+
+void CRemConSession::CompleteSendNotify()
+	{
+	LOG_FUNC;
+	LOG1(_L("\tiSendError = %d"), iSendError);
+
+	if (iSending == ESendingReliable)
+		{
+		CompleteClient(iSendMsg, iSendError);
+		}
+	
+	ASSERT_DEBUG(iSendQueue);
+	if (!iSendQueue->IsEmpty())
+		{
+		ASSERT_DEBUG(iSendNextCallBack);
+		iSendNextCallBack->CallBack();
+		}
+	iSending = ENotSending;
+	}
+
+TInt CRemConSession::SendNextCb(TAny* aThis)
+	{
+	static_cast<CRemConSession*>(aThis)->DoSendNext();
+	return KErrNone;
+	}
+
+void CRemConSession::DoSendNext()
+	{
+	ASSERT_DEBUG(iSendQueue);
+	CRemConMessage& msg = iSendQueue->First();
+	iSendQueue->Remove(msg);
+	SendToServer(msg);
+	}
+
+void CRemConSession::PanicSend(TRemConClientPanic aCode)
+	{
+	LOG_FUNC;
+	LOG1(_L("\taCode = %d"), aCode);
+
+	PANIC_MSG(iSendMsg, KRemConClientPanicCat, aCode);
+	}
+
+TBool CRemConSession::SupportedMessage(const CRemConMessage& aMsg)
+    {
+    // Return true unless this is a command for an unsupported interface
+    return !(aMsg.MsgType() == ERemConCommand && !FindInterfaceByUid(aMsg.InterfaceUid()));
+    }
+
+TInt CRemConSession::WriteMessageToClient(const CRemConMessage& aMsg)
+	{
+	LOG_FUNC;
+
+	ASSERT_DEBUG(SupportedMessage(aMsg));
+	ASSERT_DEBUG(iReceiveMsg.Handle());
+	TRAPD(err, WriteMessageToClientL(aMsg));
+	CompleteClient(iReceiveMsg, err);
+
+	LOG1(_L("\terr = %d"), err);
+	return err;
+	}
+	
+void CRemConSession::WriteMessageToClientL(const CRemConMessage& aMsg)
+	{
+	LOG_FUNC;
+	
+	//check if our client is interested in this API
+	//Only need to check commands because it is safe to assume that we are interested 
+	//in the response if we have sent out a command.
+	if(aMsg.MsgType() == ERemConCommand && !FindInterfaceByUid(aMsg.InterfaceUid()))
+		{
+		//The server will clean up the resource allocated for this msg
+		LEAVEL(KErrArgument);
+		}
+
+	// This logging code left in for maintenance.
+	//LOG1(_L("\t\tOperationData = \"%S\""), &aMsg.OperationData());
+	
+	TRemConClientReceivePackage receivePackage;
+	receivePackage.iInterfaceUid = aMsg.InterfaceUid();
+	receivePackage.iOperationId = aMsg.OperationId();
+	receivePackage.iMessageSubType = aMsg.MsgSubType();
+	receivePackage.iRemoteAddress = aMsg.Addr();
+	
+	TPckgC<TRemConClientReceivePackage> recPckg(receivePackage);
+	LEAVEIFERRORL(iReceiveMsg.Write(0, recPckg));
+	
+	// Note that we do not panic the client if their descriptor is not 
+	// big enough to hold the operation-specific data. If we did, then 
+	// a buggy remote could take down a client of RemCon. Just error 
+	// the client instead.
+	LEAVEIFERRORL(iReceiveMsg.Write(1, aMsg.OperationData()));
+	}
+	
+void CRemConSession::CheckForPendingMsg() const
+	{
+	LOG_FUNC;
+	if (iPendingMsg.Handle())
+		{
+		ASSERT_DEBUG(iPendingMsgProcessor);
+		iPendingMsgProcessor->Complete();
+		}
+	}
+
+void CRemConSession::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;
+		}
+	}
+
+
+TInt CRemConSession::SupportedInterfaces(RArray<TUid>& aUids)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iInterestedAPIs);
+	TInt err = KErrNone;
+	
+	aUids.Reset();
+	TInt count = iInterestedAPIs->Array().Count();
+	for(TInt i=0; (i<count) && (err == KErrNone); i++)
+		{
+		CRemConInterfaceDetails* details = iInterestedAPIs->Array()[i];
+		ASSERT_DEBUG(details);
+		err = aUids.Append(details->Uid());
+		}
+	
+	return err;
+	}
+
+TInt CRemConSession::SupportedBulkInterfaces(RArray<TUid>& aUids)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iInterestedAPIs);
+	TInt err = KErrNone;
+	
+	aUids.Reset();
+	TInt count = iInterestedAPIs->Array().Count();
+	for(TInt i=0; (i<count) && (err == KErrNone); i++)
+		{
+		CRemConInterfaceDetails* details = iInterestedAPIs->Array()[i];
+		ASSERT_DEBUG(details);
+		if(details->IsBulk())
+			{
+			err = aUids.Append(details->Uid());
+			}
+		}
+	
+	return err;
+	}
+
+TInt CRemConSession::SupportedOperations(TUid aInterfaceUid, RArray<TUint>& aOperations)
+	{
+	LOG_FUNC
+	TInt err = KErrNotSupported;
+	CRemConInterfaceDetails* details = FindInterfaceByUid(aInterfaceUid);
+	
+	if(details)
+		{
+		TRAP(err, details->GetRemConInterfaceFeaturesL(aOperations));
+		}
+	return err;
+	}
+
+CRemConInterfaceDetails* CRemConSession::FindInterfaceByUid(TUid aUid) const
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iInterestedAPIs);
+	TInt count = iInterestedAPIs->Array().Count();
+	for(TInt ix=0; ix<count; ++ix)
+		{
+		CRemConInterfaceDetails* details = iInterestedAPIs->Array()[ix];
+		ASSERT_DEBUG(details);
+		if(details->Uid() == aUid)
+			{
+			return details;
+			}
+		}
+	return NULL;
+	}
+
+