// 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:
//

/**
 @file
 @internalComponent
*/

#include <bluetooth/logger.h>
#include <remconinterfaceselector.h>
#include <remcon/remconinterfacebase.h>
#include <remconerrorobserver.h>
#include <remcon/remconinterfacefeatures.h>
#include <remcon/remconifdetails.h>
#include <s32mem.h>
#include "bulkreceiver.h"
#include "receiver.h"
#include "remconclient.h"
#include "remconbulkclient.h"
#include "utils.h"


const TInt KMaxSharedThreadHeapSize = 0x0400;


#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_REMCON_IF_SEL);
#endif

#ifdef _DEBUG
PANICCATEGORY("ifsel");
#endif

#define RCIS_VERBOSE_PANIC(code) \
	{ \
	LOG1(_L("Panicking with RemConIfSel / %d"), code); \
	User::Panic(KRemConIfSelPanicCat, code); \
	}

#define RCIS_VERBOSE_ASSERT(cond, code) \
	{ \
	if ( !(cond) ) \
		{ \
		RCIS_VERBOSE_PANIC(code); \
		} \
	}

void CloseDeleteAndNull(TAny* aPtr)
	{
	RRemCon** sessionPtrPtr = static_cast<RRemCon**>(aPtr);
	RRemCon* session = *sessionPtrPtr;
	session->Close();
	delete session;
	*sessionPtrPtr = NULL;
	}

void CleanupCloseDeleteAndNullPushL(RRemCon** aSession)
	{
	TCleanupItem item(CloseDeleteAndNull, aSession);
	CleanupStack::PushL(item);	
	}

EXPORT_C CRemConInterfaceSelector* CRemConInterfaceSelector::NewL()
	{
	CONNECT_LOGGER
	LOG_STATIC_FUNC

	CRemConInterfaceSelector* ifSel = new(ELeave) CRemConInterfaceSelector;
	CleanupStack::PushL(ifSel);
	ifSel->ConstructL();
	CleanupStack::Pop(ifSel);
	return ifSel;
	}

void CRemConInterfaceSelector::ConstructL()
	{
	iSharedThreadHeap = UserHeap::ChunkHeap(NULL, 0, KMaxSharedThreadHeapSize);
	if(!iSharedThreadHeap)
		{
		LEAVEL(KErrNoMemory);
		}
	iBulkCleanupCall = new(ELeave) RSpecificThreadCallBack;
	TCallBack bulkCleanupCb(StaticBulkCleanup, this);
	LEAVEIFERRORL(iBulkCleanupCall->Create(bulkCleanupCb, CActive::EPriorityStandard));
	
	// allocate in the shared objects heap.
	RHeap* currentHeap = User::SwitchHeap(iSharedThreadHeap);
	CleanupSwitchHeapPushL(*currentHeap);
		{
		iInterfaces = CRemConInterfaceDetailsArray::NewL();
		}
	CleanupStack::PopAndDestroy(currentHeap);
	
	iLock = new (ELeave) RNestableLock();
	CleanupStack::PushL(iLock);
	LEAVEIFERRORL(iLock->CreateLocal());
	CleanupStack::Pop(iLock);
	}

CRemConInterfaceSelector::CRemConInterfaceSelector()
	{
	LOG_FUNC
	iTargetReceiver = NULL;
	}

EXPORT_C CRemConInterfaceSelector::~CRemConInterfaceSelector()
	{
	LOG_FUNC
	
	// The easy one... (i.e. non-bulk interfaces)
	for(TInt ix = 0; ix < iInterfaces->Array().Count(); ++ix)
		{
		CRemConInterfaceDetails* const details = iInterfaces->Array()[ix];
		ASSERT_DEBUG(details);
		if(!details->IsBulk())
			{
			CRemConInterfaceBase* interface = details->Interface();
			details->Interface() = NULL;
			delete interface;
			}
		}
	
	// The tricky one...
	// we have to use the thread specific cleanup because they have to 
	// cancel some objects...
	TInt err = iBulkCleanupCall->CallBack();
	if(err == KErrDied)
		{
		// If the other thread is dead then we cannot cleanly cleanup
		// but if the heap is shared then we should be ok.
		if(iBulkHeap == &User::Heap())
			{
			BulkCleanup();
			}
		}
	
	// Finally tidy-up shared thread objects.
	RHeap* currentHeap = User::SwitchHeap(iSharedThreadHeap);
		{
		delete iInterfaces;
		}
	User::SwitchHeap(currentHeap);

	
	delete iTargetReceiver;
	delete iControllerReceiver;
	// delete iBulkReceiver; // This is done in the bulk thread cleanup.

	if(iControllerSession)
		{
		iControllerSession->Close();
		delete iControllerSession;
		}

	if(iTargetSession)
		{
		iTargetSession->Close();
		delete iTargetSession;
		}

	// iBulkSession.Close(); // This is done in the bulk thread cleanup.

	iBulkCleanupCall->Close();
	delete iBulkCleanupCall;
	
	iSharedThreadHeap->Close();
	
	iBulkThread.Close();

	iLock->Wait();
	iLock->Close();
	delete iLock;
	
	CLOSE_LOGGER
	}

TInt CRemConInterfaceSelector::StaticBulkCleanup(TAny* aSelf)
	{
	LOG_STATIC_FUNC
	reinterpret_cast<CRemConInterfaceSelector*>(aSelf)->BulkCleanup();
	return KErrNone;
	}

void CRemConInterfaceSelector::BulkCleanup()
	{
	LOG_FUNC
	CBulkReceiver* recv = iBulkReceiver;
	iBulkReceiver = NULL;
	delete recv;
	if(RThread().Id() == iBulkThread.Id() && iBulkSession)
		{
		iBulkSession->Close();
		delete iBulkSession;
		iBulkSession = NULL;
		}
	for(TInt ix = 0; ix < iInterfaces->Array().Count(); ++ix)
		{
		CRemConInterfaceDetails* const details = iInterfaces->Array()[ix];
		ASSERT_DEBUG(details);
		if(details->IsBulk())
			{
			CRemConInterfaceBase* interface = details->Interface();
			details->Interface() = NULL;
			delete interface;
			}
		}
	}

void CRemConInterfaceSelector::EstablishBulkThreadBindingL()
	{
	LOG_FUNC
	if(iBulkHeap)
		{
		// Already bound
		RCIS_VERBOSE_ASSERT(RThread().Id() == iBulkThread.Id(), ERemConIfSelMultipleBulkInterfaceThreads);
		}
	else
		{
		// Create Binding.
		LEAVEIFERRORL(iBulkThread.Duplicate(RThread()));
		CleanupClosePushL(iBulkThread);
		LEAVEIFERRORL(iBulkCleanupCall->Start());
		iBulkReceiver = CBulkReceiver::NewL(*this);	
		iBulkHeap = &User::Heap();
		CleanupStack::Pop(&iBulkThread);
		}
	
	}

void CRemConInterfaceSelector::RegisterInterfaceCommonL(CRemConInterfaceBase& aInterface, const TDesC8& aFeatures)
	{
	LOG_FUNC
	
	const TBool bulkIf = aInterface.Bulk();
	if(bulkIf)
		{
		EstablishBulkThreadBindingL();
		}

	// Check an instance of the same interface (same interface UID and type) 
	// has not already been added.
	const TUint count = iInterfaces->Array().Count();
	for(TUint ii=0; ii<count; ++ii)
		{
		CRemConInterfaceBase* const iface = iInterfaces->Array()[ii]->Interface();
		RCIS_VERBOSE_ASSERT(iface, ERemConIfSelInternalError);
		RCIS_VERBOSE_ASSERT( (iface->InterfaceUid() != aInterface.InterfaceUid()) || (iface->Type() != aInterface.Type()), 
			ERemConIfSelInterfaceOfThatTypeAlreadyRegistered);
		}

	// Registration of interfaces should occur before we try to use any of 
	// them.
	RCIS_VERBOSE_ASSERT(!TargetOpened() && !ControllerOpened(), ERemConIfSelTardyInterfaceRegistration);
	
	iLock->Wait();	// critical session
	RHeap* currentHeap = User::SwitchHeap(iSharedThreadHeap);
	CleanupSwitchHeapPushL(*currentHeap);
		{
		CRemConInterfaceDetails* details = CRemConInterfaceDetails::NewLC(aInterface.InterfaceUid(), aInterface.Type(), bulkIf, &aInterface, aFeatures);
		iInterfaces->AppendL(details);
		CleanupStack::Pop(details);
		}
	CleanupStack::PopAndDestroy(currentHeap);
	iLock->Signal(); // end of critical session
	
	// The interface has been appended OK, so adjust iMaxDataLength.
	const TUint interfaceMaxLength = aInterface.MaxLength();
	if(!bulkIf && interfaceMaxLength > iControlMaxDataLength)
		{
		iControlMaxDataLength = interfaceMaxLength;
		}
	else if(bulkIf && interfaceMaxLength > iBulkMaxDataLength)
		{
		iBulkMaxDataLength = interfaceMaxLength;
		}
	}

EXPORT_C void CRemConInterfaceSelector::RegisterInterfaceL(CRemConInterfaceBase& aInterface)
	{
	LOG_FUNC
	
	RegisterInterfaceCommonL(aInterface, KNullDesC8);
	}

void CRemConInterfaceSelector::RegisterInterfaceL(CRemConInterfaceBase& aInterface, RRemConInterfaceFeatures& aRemConInterfaceFeatures)
	{
	LOG_FUNC

	RegisterInterfaceCommonL(aInterface, aRemConInterfaceFeatures.SupportedOperations());
	}

EXPORT_C void CRemConInterfaceSelector::RegisterErrorObserver(MRemConErrorObserver* aObserver)
	{
	LOG_FUNC
	LOG1(_L("\taObserver = 0x%08x"), aObserver);
	
	iErrorObserver = aObserver;
	}

EXPORT_C void CRemConInterfaceSelector::OpenControllerL()
	{
	LOG_FUNC

	// An attempt to open a controller session when one is already open 
	// is an exceptional condition.
	if (ControllerOpened())
		{
		LEAVEL(KErrInUse);
		}

	// NB We don't enforce that there are some interfaces registered here 
	// because the client might be connecting just to watch connection 
	// statuses.

	iControllerSession = new(ELeave)RRemConController();
	LEAVEIFERRORL(iControllerSession->Connect());
	CleanupCloseDeleteAndNullPushL(reinterpret_cast<RRemCon**>(&iControllerSession));
	
	RegisterInterestedApisL(ERemConClientTypeController);

	// Now there's a session to receive on, start the receiver.
	RCIS_VERBOSE_ASSERT(!iControllerReceiver, ERemConIfSelInternalError);
	iControllerReceiver = CReceiver::NewL(*iControllerSession, *this, iControlMaxDataLength, ERemConClientTypeController);
	CleanupStack::Pop(&iControllerSession);
	}

void CRemConInterfaceSelector::RegisterInterestedApisL(TRemConClientType aType)
	{
	LOG_FUNC

	TBool target = CRemConInterfaceBase::Target(aType);
	
	// First get the size of the data we are going to pass to the server.
	RCountSizeWriteStream counter;
	iInterfaces->ExternalizeL(counter, aType);
	TInt size = counter.Size();
	counter.Close();
	
	// Create a suitable size buffer.
	RBuf8 ipcBuf;
	ipcBuf.CreateL(size);
	ipcBuf.CleanupClosePushL();
	
	// Encode the interface details into the buffer.
	RDesWriteStream ipcStream(ipcBuf);
	iInterfaces->ExternalizeL(ipcStream, aType);
	ipcStream.CommitL();
	ipcStream.Close();
	
	// Inform the correct session about the interfaces
	if(!target)
		{
		LEAVEIFERRORL(iControllerSession->RegisterInterestedAPIs(ipcBuf));
		}
	else
		{	
		LEAVEIFERRORL(iTargetSession->RegisterInterestedAPIs(ipcBuf));
		}
	
	CleanupStack::PopAndDestroy(&ipcBuf);
	}

void CRemConInterfaceSelector::OpenTargetCommonL()
	{
	LOG_FUNC

	// NB We don't enforce that there are some interfaces registered here 
	// because the client might be connecting just to watch connection 
	// statuses.
	CleanupCloseDeleteAndNullPushL(reinterpret_cast<RRemCon**>(&iTargetSession));

	RegisterInterestedApisL(ERemConClientTypeTarget);
	
	// Now we are finished with the features, so we can release some memory.
	RHeap* currentHeap = User::SwitchHeap(iSharedThreadHeap);
		{
		for(TInt ix = 0; ix < iInterfaces->Array().Count(); ++ix)
			{
			CRemConInterfaceDetails* const details = iInterfaces->Array()[ix];
			ASSERT_DEBUG(details);
			details->DeleteFeatures();
			}
		}
	User::SwitchHeap(currentHeap);
	
	// Now there's a session to receive on, start the receiver.
	RCIS_VERBOSE_ASSERT(!iTargetReceiver, ERemConIfSelInternalError);
	iTargetReceiver = CReceiver::NewL(*iTargetSession, *this, iControlMaxDataLength, ERemConClientTypeTarget);
	CleanupDeleteAndNullPushL(iTargetReceiver);

	if(iBulkReceiver)
		{
		// We delegate the call to the thread the receiver is running
		// in - waiting until it has completed (with success or error).
		iBulkReceiver->WaitUntilConnectedL();
		}

	CleanupStack::Pop(2, &iTargetSession); // iTargetReceiver, iTargetSession
	}

EXPORT_C void CRemConInterfaceSelector::OpenTargetL()
	{
	LOG_FUNC

	// An attempt to open a target session when one is already open 
	// is an exceptional condition.
	if (TargetOpened())
		{
		LEAVEL(KErrInUse);
		}
	
	iTargetSession = new(ELeave)RRemConTarget();
	TInt err = iTargetSession->Connect();
	if(err == KErrNone)
		{
		OpenTargetCommonL();
		}
	else
		{
		delete iTargetSession;
		iTargetSession = NULL;
		LEAVEL(err);
		}
	}

/**
This should be run from the thread in which the bulk interfaces are to run.
*/
void CRemConInterfaceSelector::BulkSessionConnectL()
	{
	LOG_FUNC
	iBulkSession = new(ELeave)RRemConBulk();
	CleanupStack::PushL(iBulkSession);
	LEAVEIFERRORL(iBulkSession->Connect());
	CleanupStack::Pop(iBulkSession);

	CleanupCloseDeleteAndNullPushL(reinterpret_cast<RRemCon**>(&iBulkSession));
	RCIS_VERBOSE_ASSERT(iBulkReceiver, ERemConIfSelInternalError);
	iBulkReceiver->InitialiseL(*iBulkSession, iBulkMaxDataLength);
	CleanupStack::Pop(&iBulkSession);
	}

/**
Opens a target session to RemCon.

If any bulk interfaces have been registered on this interface selector the
the thread in which the first bulk interface was created must be ready to run and
not blocked waiting for the completion of this function.  Failure to do so will lead
to deadlock.

@param aPlayerType The type of player
@param aPlayerSubType The sub-type of the player
@param aPlayerName  The name of the player
@leave KErrInUse If a target session is already open.
*/
EXPORT_C void CRemConInterfaceSelector::OpenTargetL(TPlayerType aPlayerType, TPlayerSubType aPlayerSubType, const TDesC8& aPlayerName)
	{
	LOG_FUNC

	// An attempt to open a target session when one is already open 
	// is an exceptional condition.
	if (TargetOpened())
		{
		LEAVEL(KErrInUse);
		}

	iTargetSession = new(ELeave)RRemConTarget();
	TInt err = iTargetSession->Connect(aPlayerType,aPlayerSubType,aPlayerName);
	if(err == KErrNone)
		{
		OpenTargetCommonL();
		}
	else
		{
		delete iTargetSession;
		iTargetSession = NULL;
		LEAVEL(err);
		}
	}

EXPORT_C TBool CRemConInterfaceSelector::ControllerOpened() const
	{
	ASSERT_DEBUG((iControllerSession && iControllerSession->Handle()) || !iControllerSession);
	return iControllerSession ? ETrue : EFalse;
	}

EXPORT_C TBool CRemConInterfaceSelector::TargetOpened() const
	{
	ASSERT_DEBUG((iTargetSession && iTargetSession->Handle()) || !iTargetSession);
	return iTargetSession ? ETrue : EFalse;
	}

TBool CRemConInterfaceSelector::BulkOpened() const
	{
	ASSERT_DEBUG((iBulkSession && iBulkSession->Handle()) || !iBulkSession);
	return iBulkSession ? ETrue : EFalse;
	}

EXPORT_C void CRemConInterfaceSelector::GoConnectionOrientedL(const TRemConAddress& aConnection)
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	LEAVEIFERRORL(iControllerSession->GoConnectionOriented(aConnection));
	
	// Store the address.  This means that if the server dies we know all
	// we need to to return the existing sessions to usability without 
	// troubling the app.
	iAddress = aConnection;
	}

EXPORT_C void CRemConInterfaceSelector::GoConnectionlessL()
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	LEAVEIFERRORL(iControllerSession->GoConnectionless());
	
	// Unset any stored address, so we know we are connectionless.
	iAddress.BearerUid() = KNullUid;
	}

EXPORT_C void CRemConInterfaceSelector::ConnectBearer(TRequestStatus& aStat)
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	iControllerSession->ConnectBearer(aStat);
	}

EXPORT_C TInt CRemConInterfaceSelector::ConnectBearerCancel()
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	//Ignore Return code because
	// a) It'll mostly be other than KErrNone because the server has terminated, in which
	//    case the original async request will have completed with the error anyway!
	// b) It's meaningless to the client whatever the return code is.
	(void)iControllerSession->ConnectBearerCancel();
	
	return KErrNone;
	}

EXPORT_C void CRemConInterfaceSelector::DisconnectBearer(TRequestStatus& aStat)
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	iControllerSession->DisconnectBearer(aStat);
	}

EXPORT_C TInt CRemConInterfaceSelector::DisconnectBearerCancel()
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);

	//See CRemConInterfaceSelector::ConnectBearerCancel() for comment
	(void)iControllerSession->DisconnectBearerCancel();

	return KErrNone;
	}

EXPORT_C void CRemConInterfaceSelector::Send(TRequestStatus& aStatus, 
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 TUint& aNumRemotes,
											 TRemConMessageType aMsgType,
											 const TDesC8& aData)
	{
	LOG_FUNC

	Send(aStatus, aInterfaceUid, aOperationId, aNumRemotes, aMsgType, ERemConMessageDefault, aData);
	}

EXPORT_C void CRemConInterfaceSelector::Send(TRequestStatus& aStatus, 
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 TUint& aNumRemotes,
											 TRemConMessageType aMsgType,
											 TRemConMessageSubType aSubType,
											 const TDesC8& aData)
	{
	LOG_FUNC
	
	switch ( aMsgType )
		{
	case ERemConCommand:
	case ERemConNotifyCommand:
		RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);
		iControllerSession->Send(aStatus, aInterfaceUid, aOperationId, aNumRemotes, aSubType, aData);
		break;
		
	case ERemConResponse:
		RCIS_VERBOSE_ASSERT(TargetOpened(), ERemConIfSelNoTargetSession);
		iTargetSession->Send(aStatus, aInterfaceUid, aOperationId, aNumRemotes, aSubType, aData);
		break;
		
	default:
		RCIS_VERBOSE_PANIC(ERemConIfSelBadMessageType);
		break;
		}
	}

/**
Sends a notify command to the remote device.

@see CRemConInterfaceSelector::Send()
*/
EXPORT_C void CRemConInterfaceSelector::SendNotify(TRequestStatus& aStatus, 
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 TRemConMessageType aMsgType,
											 TRemConMessageSubType aSubType,
											 const TDesC8& aData)
	{
	LOG_FUNC
	if (aMsgType == ERemConNotifyCommand)
		{
		RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);
		iControllerSession->SendNotify(aStatus, aInterfaceUid, aOperationId, aSubType, aData);
		}
	else
		{
		RCIS_VERBOSE_PANIC(ERemConIfSelBadMessageType);
		}
	}

EXPORT_C TInt CRemConInterfaceSelector::SendUnreliable(
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 TRemConMessageType aMsgType,
											 const TDesC8& aData)
	{
	LOG_FUNC
	
	return SendUnreliable(aInterfaceUid, aOperationId, aMsgType, ERemConMessageDefault, aData);
	}

EXPORT_C TInt CRemConInterfaceSelector::SendUnreliable(
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 TRemConMessageType aMsgType,
											 TRemConMessageSubType aSubType,
											 const TDesC8& aData)
	{
	LOG_FUNC
	
	TInt ret = KErrNone;
	
	switch ( aMsgType )
		{
		case ERemConCommand:
		case ERemConNotifyCommand:
		RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);
		ret = iControllerSession->SendUnreliable(aInterfaceUid, aOperationId, aSubType, aData);
		break;
		
		case ERemConResponse:
		RCIS_VERBOSE_ASSERT(TargetOpened(), ERemConIfSelNoTargetSession);
		ret = iTargetSession->SendUnreliable(aInterfaceUid, aOperationId, aSubType, aData);
		break;
		
		default:
		RCIS_VERBOSE_PANIC(ERemConIfSelBadMessageType);
		break;
		}
	return ret;
	}

EXPORT_C TInt CRemConInterfaceSelector::SendCancel(TRemConMessageType aMsgType)
	{
	LOG_FUNC

	switch ( aMsgType )
		{
	case ERemConCommand:
	case ERemConNotifyCommand:
		RCIS_VERBOSE_ASSERT(ControllerOpened(), ERemConIfSelNoControllerSession);
		//See CRemConInterfaceSelector::ConnectBearerCancel() for comment
		(void)iControllerSession->SendCancel();
		break;

	case ERemConResponse:
		RCIS_VERBOSE_ASSERT(TargetOpened(), ERemConIfSelNoTargetSession);
		//See CRemConInterfaceSelector::ConnectBearerCancel() for comment
		(void)iTargetSession->SendCancel();
		break;

	default:
		RCIS_VERBOSE_PANIC(ERemConIfSelBadMessageType);
		break;
		}

	return KErrNone;
	}

EXPORT_C void CRemConInterfaceSelector::SendBulk(TRequestStatus& aStatus, 
											 TUid aInterfaceUid, 
											 TUint aOperationId, 
											 const TDesC8& aData)
	{
	LOG_FUNC
	
	// Panic as Target Session, because bulkness is transparent to client
	RCIS_VERBOSE_ASSERT(BulkOpened(), ERemConIfSelNoTargetSession);
	iBulkSession->Send(aStatus, aInterfaceUid, aOperationId, aData);
	}

EXPORT_C TInt CRemConInterfaceSelector::SendBulkUnreliable(
											TUid aInterfaceUid,
											TUint aOperationId,
											const TDesC8& aData)
	{
	LOG_FUNC
	
	// Panic as Target Session, because bulkness is transparent to client
	RCIS_VERBOSE_ASSERT(BulkOpened(), ERemConIfSelNoTargetSession);
	return iBulkSession->SendUnreliable(aInterfaceUid, aOperationId, aData);
	}

EXPORT_C TInt CRemConInterfaceSelector::SendBulkCancel()
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(BulkOpened(), ERemConIfSelNoTargetSession);
	//See CRemConInterfaceSelector::ConnectBearerCancel() for comment
	(void)iBulkSession->SendCancel();

	return KErrNone;
	}

void CRemConInterfaceSelector::ReceiveComplete(TUid aInterfaceUid, 
		TUint aOperationId, 
		TRemConMessageSubType aMsgSubType, 
		const TRemConAddress& aRemoteAddress,
		const TDesC8& aData, 
		TRemConClientType aType)
	{
	LOG_FUNC
	LOG1(_L("\taInterfaceUid = 0x%08x"), aInterfaceUid);
	LOG1(_L("\taOperationId = 0x%02x"), aOperationId);
	LOG1(_L("\taRemoteAddress.BearerUid = 0x%08x"), aRemoteAddress.BearerUid());
	
	const TUint count = iInterfaces->Array().Count();
	for ( TUint ii = 0 ; ii < count ; ++ii )
		{
		CRemConInterfaceDetails* const details = iInterfaces->Array()[ii];
		ASSERT_DEBUG(details);
		CRemConInterfaceBase* const iface = details->Interface();
		RCIS_VERBOSE_ASSERT(iface, ERemConIfSelInternalError);

		if (	iface->InterfaceUid() == aInterfaceUid 
			&&	iface->Type() == aType )
			{
			ASSERT_DEBUG(!details->IsBulk());
			MRemConInterfaceIf3* interfaceIf3 = reinterpret_cast<MRemConInterfaceIf3*>(iface->GetInterfaceIf(TUid::Uid(KRemConInterfaceIf3)));
			if (interfaceIf3)
				{
				interfaceIf3->MrcibNewMessage(aOperationId, aData, aMsgSubType, aRemoteAddress);
				break;
				}
			MRemConInterfaceIf2* interfaceIf2 = reinterpret_cast<MRemConInterfaceIf2*>(iface->GetInterfaceIf(TUid::Uid(KRemConInterfaceIf2)));
			if (interfaceIf2)
				{
				interfaceIf2->MrcibNewMessage(aOperationId, aData, aMsgSubType);
				break;
				}
			MRemConInterfaceIf* interfaceIf = reinterpret_cast<MRemConInterfaceIf*>(iface->GetInterfaceIf(TUid::Uid(KRemConInterfaceIf1)));
			RCIS_VERBOSE_ASSERT(interfaceIf, ERemConIfSelNoInterfaceImplementation);
			interfaceIf->MrcibNewMessage(aOperationId, aData);
			break;
			}
		}
	}

void CRemConInterfaceSelector::BulkReceiveComplete(TUid aInterfaceUid, TUint aOperationId, const TDesC8& aData)
	{
	LOG_FUNC
	LOG1(_L("\taInterfaceUid = 0x%08x"), aInterfaceUid);
	LOG1(_L("\taOperationId = 0x%02x"), aOperationId);

	const TUint count = iInterfaces->Array().Count();
	for ( TUint ii = 0 ; ii < count ; ++ii )
		{
		CRemConInterfaceDetails* const details = iInterfaces->Array()[ii];
		ASSERT_DEBUG(details);
		CRemConInterfaceBase* const iface = details->Interface();
		RCIS_VERBOSE_ASSERT(iface, ERemConIfSelInternalError);

		if(details->IsBulk() && iface->InterfaceUid() == aInterfaceUid)
			{
			MRemConInterfaceIf* interfaceIf = reinterpret_cast<MRemConInterfaceIf*>(iface->GetInterfaceIf(TUid::Uid(KRemConInterfaceIf1)));
			RCIS_VERBOSE_ASSERT(interfaceIf, ERemConIfSelNoInterfaceImplementation);
			interfaceIf->MrcibNewMessage(aOperationId, aData);
			}
		}
	}

void CRemConInterfaceSelector::Error(TInt aError)
	{
	LOG_FUNC
	LOG1(_L("\taError = %d"), aError);
	LOG1(_L("\tiErrorObserver = 0x%08x"), iErrorObserver);
	
	if(aError == KErrServerTerminated)
		{
		// Initially try and deal with server death in a way that is
		// transparent to the app.
		TInt err = TryToReconnect();
		LOG1(_L("\tTryToReconnect error = %d"), err);
	
		// If we fail inform any registered app.  Unregistered apps
		// just take the risk that they may not realise the server has
		// died if it errors on a receive.
		if(err && iErrorObserver)
			{
			iErrorObserver->MrceoError(KErrServerTerminated);
			}
		}
	}

void CRemConInterfaceSelector::BulkError(TInt aError)
	{
	LOG_FUNC
	LOG1(_L("\taError = %d"), aError);
	LOG1(_L("\tiErrorObserver = 0x%08x"), iErrorObserver);
	
	if(aError == KErrServerTerminated)
		{
		// Initially try and deal with server death in a way that is
		// transparent to the app.
		TInt err = TryToReconnectBulk();
		LOG1(_L("\tTryToReconnectBulk error = %d"), err);
	
		// If we fail inform any registered app.  Unregistered apps
		// just take the risk that they may not realise the server has
		// died if it errors on a receive.
		if(err && iErrorObserver)
			{
			iErrorObserver->MrceoError(KErrServerTerminated);
			}
		}
	}

EXPORT_C TInt CRemConInterfaceSelector::GetConnections(TSglQue<TRemConAddress>& aConnections)
	{
	LOG_FUNC

	// It doesn't matter which session we use for this.
	RRemCon* sess = ControllerOpened() ? reinterpret_cast<RRemCon*>(iControllerSession) : reinterpret_cast<RRemCon*>(iTargetSession);
	AssertSession(sess, ERemConIfSelNoSession);

	return sess->GetConnections(aConnections);
	}

EXPORT_C void CRemConInterfaceSelector::NotifyConnectionsChange(TRequestStatus& aStatus)
	{
	LOG_FUNC

	// It doesn't matter which session we use for this.
	iNotificationSession = ControllerOpened() ? reinterpret_cast<RRemCon*>(iControllerSession) : reinterpret_cast<RRemCon*>(iTargetSession);
	AssertSession(iNotificationSession, ERemConIfSelNoSession);

	iNotificationSession->NotifyConnectionsChange(aStatus);
	}

EXPORT_C TInt CRemConInterfaceSelector::NotifyConnectionsChangeCancel()
	{
	LOG_FUNC

	// Get the session we used for posting the original notification. It won't 
	// have gone away in the meantime as that only happens when 'this' is 
	// destroyed, but the client may call this without ever having called the 
	// original notification.
	AssertSession(iNotificationSession, ERemConIfSelNoSession);

	//See CRemConInterfaceSelector::ConnectBearerCancel() for comment
	(void)iNotificationSession->NotifyConnectionsChangeCancel();

	return KErrNone;
	}

void CRemConInterfaceSelector::AssertSession(RRemCon* aSess, TInt aPanicCode) const
	{
	LOG_FUNC

	RCIS_VERBOSE_ASSERT(aSess && aSess->Handle(), aPanicCode);
	}

/** 
Resurrects sessions in case of server death.

For the target and controller session we try to open a new
session.  If this succeeds we can then set the old session
to point to the new one after closing our old defunct 
session.

On success we are left with happy functioning handles.
On failure we are left with defunct handles, which are still 
valid but will complete all requests with KErrServerTerminated.

This means that we won't cause an application to get a bad handle
panic if it hasn't done anything wrong.
*/
TInt CRemConInterfaceSelector::TryToReconnect()
	{
	LOG_FUNC
	
	// Always want to stop receiving.  The receiver itself should stop issuing
	// further receives on completion, but we only want to have the error function
	// called once, so Cancel() receivers in case we had both.
	if(TargetOpened())
		{
		RCIS_VERBOSE_ASSERT(iTargetReceiver, ERemConIfSelInternalError);
		iTargetReceiver->Cancel();
		}

	if(ControllerOpened())
		{
		RCIS_VERBOSE_ASSERT(iControllerReceiver, ERemConIfSelInternalError);
		iControllerReceiver->Cancel();
		}
		
	// Now try and create new sessions.  We do all the failable work first
	// so we aren't left in a half alive situation.		
	TInt err = KErrNone;
	RRemConTarget newTarget;
	RRemConController newController;
	
	if(TargetOpened())
		{
		// See if we can kick RemCon back into life
		err = newTarget.Connect();
		}
	
	if(!err && ControllerOpened())
		{		
		// See if we can kick RemCon back into life
		err = newController.Connect();
		
		if(!err && !iAddress.IsNull())
			{	
			// If an address is set the session was connection oriented, 
			// resurrect that now.	
			err = newController.GoConnectionOriented(iAddress);
			}
		}
	
	if(!err)
		{
		// RemCon lives!  Set our sessions to be the nice new ones.
		if(TargetOpened())
			{
			iTargetSession->Close();
			iTargetSession->SetHandle(newTarget.Handle());
			iTargetReceiver->Receive();
			}

		if(ControllerOpened())
			{
			iControllerSession->Close();
			iControllerSession->SetHandle(newController.Handle());
			iControllerReceiver->Receive();
			}
		}
	else
		{
		// We may not have successfully opened these, but it's safe to 
		// close them anyway.
		newTarget.Close();
		newController.Close();
		}
	
	return err;
	}



/** 
Resurrects bulk sessions in case of server death.

For the bulk session we try to open a new
session.  If this succeeds we can then set the old session
to point to the new one after closing our old defunct 
session.

On success we are left with happy functioning handles.
On failure we are left with defunct handles, which are still 
valid but will complete all requests with KErrServerTerminated.

This means that we won't cause an application to get a bad handle
panic if it hasn't done anything wrong.
*/
TInt CRemConInterfaceSelector::TryToReconnectBulk()
	{
	LOG_FUNC
	
	// Always want to stop receiving.  The receiver itself should stop issuing
	// further receives on completion, but we only want to have the error function
	// called once, so Cancel() receivers in case we had both.
	if(BulkOpened())
		{
		RCIS_VERBOSE_ASSERT(iBulkReceiver, ERemConIfSelInternalError);
		iBulkReceiver->Cancel();
		}
		
	// Now try and create new sessions.  We do all the failable work first
	// so we aren't left in a half alive situation.		
	TInt err = KErrNone;
	RRemConBulk newBulk;
	
	if(BulkOpened())
		{
		// See if we can kick RemCon back into life
		err = newBulk.Connect();
		}
	
	if(err == KErrNone)
		{
		// RemCon lives!  Set our session to be the nice new one.
		if(BulkOpened())
			{
			iBulkSession->Close();
			iBulkSession->SetHandle(newBulk.Handle());
			iBulkReceiver->Receive();
			}
		}
	else
		{
		// We may not have successfully opened this, but it's safe to 
		// close it anyway.
		newBulk.Close();
		}
	
	return err;
	}

