remotecontrol/remotecontrolfw/server/src/session.cpp
changeset 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remotecontrol/remotecontrolfw/server/src/session.cpp	Wed Oct 13 16:20:29 2010 +0300
@@ -0,0 +1,898 @@
+// Copyright (c) 2004-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:
+// 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 "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(CRemConServer& aServer,
+		CBearerManager& aBearerManager,
+		TUint aId)
+ :	iServer(aServer),
+	iBearerManager(aBearerManager),
+	iId(aId)
+	{
+	LOG_FUNC;
+	}
+
+void CRemConSession::BaseConstructL(const TClientInfo& aClientInfo)
+	{
+	LOG_FUNC;
+
+	iClientInfo = aClientInfo;
+
+	iSendQueue = CMessageQueue::NewL();
+
+	// The send callback is used by the base class to handle queued sends.
+	iSendNextCallBack = new(ELeave) CAsyncCallBack(CActive::EPriorityStandard);
+	TCallBack cb(SendNextCb, this);
+	iSendNextCallBack->Set(cb);
+
+	}
+
+CRemConSession::~CRemConSession()
+	{
+	LOG(KNullDesC8);
+	LOG_FUNC;
+	
+	delete iSendNextCallBack;
+	delete iSendQueue;
+	delete iInterestedAPIs;
+	}
+
+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 ERemConSetPlayerType:
+		SetPlayerType(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 ERemConSend:
+		Send(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;
+		
+	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 ERemConSendNotify:
+		SendNotify(aMessage);
+		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::GetPlayerTypeAndNameL(const RMessage2& aMessage, TPlayerTypeInformation& aPlayerType, RBuf8& aPlayerName)
+	{
+	// check validity of descriptors
+	if (aMessage.GetDesLength(1) < 0 || aMessage.GetDesLength(2) < 0)
+		{
+		LEAVEL(KErrBadDescriptor);
+		}
+
+	// Retrieve and validate the client type information 
+	TPckg<TPlayerTypeInformation> pckg(aPlayerType);
+	aMessage.ReadL(1, pckg);
+	switch (aPlayerType.iPlayerType)
+		{
+	case ERemConAudioPlayer:
+		// Valid
+	case ERemConVideoPlayer:
+		// Valid
+	case ERemConBroadcastingAudioPlayer:
+		// Valid
+	case ERemConBroadcastingVideoPlayer:
+		// Valid
+		break;
+	default:
+		// Invalid
+		LEAVEL(KErrArgument);
+		}
+	switch (aPlayerType.iPlayerSubType)
+		{
+	case ERemConNoSubType:
+		// Valid
+	case ERemConAudioBook:
+		// Valid
+	case ERemConPodcast:
+		// Valid
+		break;
+	default:
+		// Invalid
+		LEAVEL(KErrArgument);
+		}
+
+	// Retrieve the client player name inforamtion
+	aPlayerName.CreateL(aMessage.GetDesLengthL(2));
+	CleanupClosePushL(aPlayerName);
+	aMessage.ReadL(2, aPlayerName);	
+	CleanupStack::Pop(&aPlayerName);
+	}
+
+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 features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		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;
+
+	// Prepare the message for send. If DoPrepareSendMessageL() returns
+	// NULL, it panicked the client.
+	CRemConMessage* msg = NULL;
+	TRAPD(err, msg = DoPrepareSendMessageL(aMessage));
+
+	if ( err != KErrNone )
+		{
+		CompleteClient(aMessage, err);
+		}
+	else if (msg)
+		{
+		ASSERT_DEBUG(iSendQueue);
+	
+		if (iSending != ENotSending || !iSendQueue->IsEmpty())
+			{
+			iSendQueue->Append(*msg);
+			}
+		else
+			{
+			// we know msg cannot be null here as said above.
+			SendToServer(*msg);
+			}
+		}
+	}
+
+void CRemConSession::SendCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		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::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 features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	iReceiveMsg = aMessage;
+
+	DoReceive();
+	}
+
+void CRemConSession::ReceiveCancel(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		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 features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		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());
+		iInGetConnectionsProcedure = ETrue;
+		}
+	CompleteClient(aMessage, err);
+	}
+
+void CRemConSession::GetConnections(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+
+	// Check we've had our features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	iInGetConnectionsProcedure = EFalse;
+
+	// 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 features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		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 features set...
+	if (!ClientAvailable())
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicClientFeaturesNotSet);
+		return;
+		}
+
+	// See comments in ConnectBearerCancel.
+	if ( iNotifyConnectionsChangeMsg.Handle() )
+		{
+		CompleteClient(iNotifyConnectionsChangeMsg, KErrCancel);
+		}
+	CompleteClient(aMessage, KErrNone);
+	}
+
+CRemConInterfaceDetailsArray* CRemConSession::ExtractInterestedAPIsL(const RMessage2& aMessage)
+	{
+	LOG_FUNC;
+	
+	CRemConInterfaceDetailsArray* result;
+	
+	RBuf8 buf;
+	buf.CreateL(aMessage.GetDesLengthL(0));
+	CleanupClosePushL(buf);
+	
+	aMessage.ReadL(0, buf);
+	RDesReadStream ipcStream(buf);
+	
+	result = CRemConInterfaceDetailsArray::InternalizeL(ipcStream);
+	
+	ipcStream.Close(); 
+	CleanupStack::PopAndDestroy(&buf);
+	
+	return result;
+	}
+
+TBool CRemConSession::DoGetSendInfoLC(const RMessage2& aMessage, 
+		TUid& aInterfaceUid,
+		TUint& aOperationId,
+		TRemConMessageSubType& aMessageSubType,
+		RBuf8& aSendDes)
+	{
+	// Get the data the client wants to send.
+	aInterfaceUid = TUid::Uid(aMessage.Int0());
+	LOG1(_L("\taInterfaceUid = 0x%08x"), aInterfaceUid);
+
+	if (aMessage.GetDesLengthL(1) != sizeof(TOperationInformation))
+		{
+		PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+		return EFalse;
+		}
+
+	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 EFalse;
+		}	
+	
+	aOperationId = opInfoPckg().iOperationId;
+	LOG1(_L("\taOperationId = 0x%02x"), aOperationId);
+	
+	aMessageSubType = opInfoPckg().iMessageSubType;
+	LOG1(_L("\taMessageSubType = 0x%02x"), aMessageSubType);
+
+	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.
+	if ( dataLength != 0 )
+		{
+		aSendDes.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)
+			aSendDes, // 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);
+			aSendDes.Close();
+			PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadDescriptor);
+			return EFalse;
+			}
+		}
+	CleanupClosePushL(aSendDes);
+	return ETrue;
+	}
+
+void CRemConSession::GoConnectionOriented(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::GoConnectionless(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::ConnectBearer(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::ConnectBearerCancel(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::DisconnectBearer(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::DisconnectBearerCancel(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::SendNotify(const RMessage2& aMessage)
+	{
+	PANIC_MSG(aMessage, KRemConClientPanicCat, ERemConClientPanicBadType);
+	}
+
+void CRemConSession::ConnectionsChanged()
+	{
+	LOG_FUNC;
+	
+	LOG1(_L("\tiInGetConnectionsProcedure = %d"), iInGetConnectionsProcedure);
+	// Only update the connections history pointer if we're not in the middle 
+	// of a 'GetConnections' procedure. 
+	if ( !iInGetConnectionsProcedure )
+		{
+		iServer.SetConnectionHistoryPointer(Id());
+		}
+	LOG1(_L("\tiNotifyConnectionsChangeMsg.Handle = %d"), iNotifyConnectionsChangeMsg.Handle());
+	if ( iNotifyConnectionsChangeMsg.Handle() )
+		{
+		CompleteClient(iNotifyConnectionsChangeMsg, KErrNone);
+		}
+	}
+
+void CRemConSession::CompleteSend()
+	{
+	LOG_FUNC;
+	LOG2(_L("\tiNumRemotes = %d, iSendError = %d"), iNumRemotes, iSendError);
+
+	ASSERT_DEBUG(NumRemotesToTry() == 0);
+	
+	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;
+	}
+
+void CRemConSession::PanicSend(TRemConClientPanic aCode)
+	{
+	LOG_FUNC;
+	LOG1(_L("\taCode = %d"), aCode);
+
+	PANIC_MSG(iSendMsg, KRemConClientPanicCat, aCode);
+	}
+
+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()));
+	}
+
+TInt CRemConSession::SupportedInterfaces(RArray<TUid>& aUids)
+	{
+	LOG_FUNC;
+
+	aUids.Reset();
+	return AppendSupportedInterfaces(aUids);
+	}
+
+TInt CRemConSession::AppendSupportedInterfaces(RArray<TUid>& aUids)
+	{
+	LOG_FUNC;
+	ASSERT_DEBUG(iInterestedAPIs);
+	TInt err = KErrNone;
+
+	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;
+
+    	aUids.Reset();
+	return AppendSupportedBulkInterfaces(aUids);
+	}
+
+TInt CRemConSession::AppendSupportedBulkInterfaces(RArray<TUid>& aUids)
+	{
+	LOG_FUNC;
+	ASSERT_DEBUG(iInterestedAPIs);
+	TInt err = KErrNone;
+
+	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;
+	}
+
+void CRemConSession::DoSendNext()
+	{
+	LOG_FUNC;
+
+	ASSERT_DEBUG(iSendQueue);
+	CRemConMessage& msg = iSendQueue->First();
+	iSendQueue->Remove(msg);
+	SendToServer(msg);
+	}
+
+TInt CRemConSession::SendNextCb(TAny *aThis)
+	{
+	LOG_STATIC_FUNC;
+
+	static_cast<CRemConSession*>(aThis)->DoSendNext();
+	return KErrNone;
+	}
+