bluetoothmgmt/btmgr/BTManServer/BTManServer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 15 Jul 2010 19:55:36 +0300
branchRCL_3
changeset 21 14e240312f6f
parent 11 20fda83a6398
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

// Copyright (c) 1999-2010 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <s32mem.h>
#include <e32svr.h>
#include "BTManServer.h"


#include "BTSec.h"
#include "BtManServerSecurityPolicy.h"
#include <bluetooth/logger.h>

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

static const TInt KMessageArrayGranularity = 4; //< The granularity of the array used to hold TBTManMessage objects
static const TInt KBTManClientServerProtocolSlot = 2; // the slot that points to the struct buf used between client and server

inline CBTManServerShutdown::CBTManServerShutdown()
	:CTimer(-1)
	{
	LOG_FUNC
	CActiveScheduler::Add(this);
	}

inline void CBTManServerShutdown::ConstructL()
	{
	LOG_FUNC
	CTimer::ConstructL();
	}

inline void CBTManServerShutdown::Start()
	{
	LOG_FUNC
	After(KBTManServerShutdownDelay);
	}

//


void CBTManServerShutdown::RunL()
/**
Initiate server exit when the timer expires
**/
	{
	LOG_FUNC
	CActiveScheduler::Stop();
	}

//=====================================================================
// CBTManServer
//=====================================================================

inline CBTManServer::CBTManServer()
	:	CPolicyServer(EPriorityStandard,KBtmanServerPolicy,ESharableSessions)
	{
	LOG_FUNC
	}

CServer2* CBTManServer::NewLC()
	{
	LOG_STATIC_FUNC
	CBTManServer* self=new(ELeave) CBTManServer;
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

void CBTManServer::ConstructL()
	{
	LOG_FUNC
	StartL(KBTManServerName);
	//Ensure that the server will exit even if the 1st client fails to connect
	iShutdown.ConstructL();
	iShutdown.Start();

	TInt err;
	err = iProperty.Define(KPropertyUidBluetoothCategory,
						   KPropertyKeyBluetoothGetRegistryTableChange,
						   RProperty::EInt,
						   KLOCAL_SERVICES,
						   KBTMAN_SID_PROT_SERV);
						   
	FLOGIFERR(err,_L("CBTManServer::ConstructL() - iProperty.Define Failure"));
		
	err = iProperty.Define(KPropertyUidBluetoothCategory,
						   KPropertyKeyBluetoothCorruptRegistryReset,
						   RProperty::EInt,
						   KLOCAL_SERVICES,
						   KLOCAL_SERVICES_AND_NETWORK_CONTROL);
						   
	FLOGIFERR(err,_L("CBTManServer::ConstructL() - iProperty.Define Failure"));

	//Create the service providers...
	iRegistry = CBTRegistry::NewL();

	iContainerIndex = CObjectConIx::NewL();
	// don't stop the server if we can't provide this service...				   
	
	//Start Backup/Restore Manager and obtain notification of backup/restore operations.
	iBURManager=CBTManServerBURMgr::NewL(*this, this);
	}


CObjectCon* CBTManServer::NewContainerL()
/**
Return a new object container
**/
	{
	LOG_FUNC
	return iContainerIndex->CreateL();
	}

void CBTManServer::DeleteContainer(CObjectCon* aCon)
	{
	LOG_FUNC
	iContainerIndex->Remove(aCon);
	}

CBTManServer::~CBTManServer()
	{
	LOG_FUNC

	// Delete defined properties
	TInt err;
	err = iProperty.Delete(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothGetRegistryTableChange);
	FLOGIFERR(err,_L("CBTManServer::~CBTManServer() - iProperty.Delete Failure"));
	err = iProperty.Delete(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothCorruptRegistryReset);
	FLOGIFERR(err,_L("CBTManServer::~CBTManServer() - iProperty.Delete Failure"));
	// Close the RProperty handle in case it is ever attached.
	iProperty.Close();

	delete iBURManager;
	delete iRegistry;
	delete iContainerIndex;
	}

CSession2* CBTManServer::NewSessionL(const TVersion &aVersion, const RMessage2& aMessage) const
	{
	LOG_FUNC
	TVersion v(KBTManServerMajorVersionNumber,KBTManServerMinorVersionNumber,KBTManServerBuildVersionNumber);
	if (!User::QueryVersionSupported(v,aVersion))
		{
		User::Leave(KErrNotSupported);
		}
	// make new session
	return new(ELeave) CBTManSession(*iRegistry, aMessage);
	}

void CBTManServer::BUROperationStarted()
/**
A backup or restore operation has started.
Cancel the shutdown timer if it was running
**/
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iBUROperationStarted, PanicServer(EBTManBadState));

	iBUROperationStarted = ETrue;
	iShutdown.Cancel();
	}

void CBTManServer::BUROperationStopped()
/**
A backup or restore operation has finished.
Start the shutdown timer if there are no sessions running.
**/
	{
	LOG_FUNC
	__ASSERT_DEBUG(iBUROperationStarted, PanicServer(EBTManBadState));

	iBUROperationStarted = EFalse;
	TryToStartShutdownTimer();
	}

void CBTManServer::AddSession()
/**
A new session is being created
Cancel the shutdown timer if it was running
**/
	{
	LOG_FUNC
	++iSessionCount;
	if (iSessionCount > iMaxSessionCount)
		{
		iMaxSessionCount = iSessionCount;
		}
	iShutdown.Cancel();
	}

void CBTManServer::DropSession()
/**
A session is being destroyed
Start the shutdown timer if it is the last session.
**/
	{
	LOG_FUNC
	__ASSERT_DEBUG(iSessionCount > 0, PanicServer(EBTManBadState));
	
	--iSessionCount;
	TryToStartShutdownTimer();
	}

void CBTManServer::TryToStartShutdownTimer()
/**
Starts the shutdown timer if the server is able to shutdown
**/
	{
	LOG_FUNC
	
	if (iSessionCount == 0 && !iBUROperationStarted)
		{
		iShutdown.Start();
		}
	}

void CBTManServer::Publish(TUint aKey, TInt aValue)
	{
	LOG_FUNC
	TInt err;
	err = iProperty.Set(KPropertyUidBluetoothCategory,
					    aKey,
						aValue);
	FLOGIFERR(err,_L("CBTManServer::Publish() - iProperty.Set Failure"));
	}

void CBTManServer::NotifyViewChange(CBTManSubSession& aSubSessionViewOwner, const TDesC& aViewDescriptor)
	{
	LOG_FUNC
	// For views owned by subsessions.
	
	// Go through all sessions - they'll dispatch so all the subessions apart from the one calling here
	iSessionIter.SetToFirst();

	while (iSessionIter)
		{
		CBTManSession* s = static_cast<CBTManSession*>(iSessionIter++);
		s->SubSessionHasOverlappingView(aSubSessionViewOwner, aViewDescriptor);
		}
	}

void CBTManServer::NotifyViewChange(const TDesC& aViewDescriptor)
	{
	LOG_FUNC
	// For views not owned by subsessions (e.g. views owned by Backup/Restore classes)

	// Go through all sessions - they'll dispatch so all the subessions apart from the one calling here
	iSessionIter.SetToFirst();

	while (iSessionIter)
		{
		CBTManSession* s = static_cast<CBTManSession*>(iSessionIter++);
		s->SubSessionHasOverlappingView(aViewDescriptor);
		}
	}
	
void PanicClient(const RMessage2& aMessage,TInt aPanic, CBTManSession* aSession)
/**
RMessage2::Panic() also completes the message. This is:
(a) important for efficient cleanup within the kernel
(b) a problem if the message is completed a second time
**/
	{
	LOG_STATIC_FUNC
	LOG1(_L("PanicClient: Reason = %d"), aPanic);
	//remove the message from the message table so we don't complete it again
	//The message may not yet be in the table, so check for NULL before deleting it
	//This code could be more efficient, but hopefully it won't be called enough to make
	//it worthwhile improving it ;-)
	
	__DEBUGGER();

	/*** ORDER MATTERS! ***/
	//First find the message in the session's stored messages
	//This must be done BEFORE panicking the client because
	//panicking the client resets the RMessage2 handle value to
	//zero and so almost always renders the FindMessage method useless!
	CBTManMessage* m = aSession->FindMessage(aMessage);
	
	//Now panic the client - this is safer done before deleting 
	//the message from the message array, just in case the RMessage2
	//object is obtained from that array and not directly from the client
	aMessage.Panic(KBTManPanic,aPanic);
	
	//Now delete the message from the session's queue. This is vital
	//if we are not to attempt to complete on a dead message
	if (m)
		{
		//aMessage will not be usable after the next line if it is obtained from the message array
		//instead of the one directly supplied by the client
		aSession->DeleteMessage(m);
		}
	}

void PanicServer(TInt aPanic)
/**
Panic our own thread
**/
	{
	LOG_STATIC_FUNC
	LOG1(_L("PanicServer: Reason = %d"), aPanic);
	User::Panic(KBTManPanic, aPanic);
	}


static void RunServerL()
/**
Perform all server initialisation, in particular creation of the
scheduler and server and then run the scheduler
**/
	{
	LOG_STATIC_FUNC
	// create and install the active scheduler we need
	CActiveScheduler* scheduler=new(ELeave) CActiveScheduler;
	CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler);
	//
	// create the server (leave it on the cleanup stack)
	CServer2* server = CBTManServer::NewLC();
	//
	
	#ifdef __BTMANSERVER_NO_PROCESSES__
	RThread::Rendezvous(KErrNone);
	#else
	// naming the server thread after the server helps to debug panics
	// ignore error - we tried the best we could
	User::RenameThread(KBTManServerName); 
	
	RProcess::Rendezvous(KErrNone);
	#endif
	
	//
	// Ready to run
	CActiveScheduler::Start();
	//
	// Cleanup the server and scheduler
	CleanupStack::PopAndDestroy(server);
	CleanupStack::PopAndDestroy(scheduler);
	}

TInt E32Main()
/**
Main entry-point for the server process
**/
	{
	CONNECT_LOGGER
	LOG_STATIC_FUNC
	LOG(_L("BTManServer RunServer"));

	__UHEAP_MARK;
#ifdef TEST_OOM //define TEST_OOM in preprocessor definitions in project settings
	__UHEAP_SETFAIL(RHeap::ERandom, 100);
#endif
	CTrapCleanup* cleanup=CTrapCleanup::New();
	TInt r=KErrNoMemory;
	if (cleanup)
		{
		TRAP(r,RunServerL());
		delete cleanup;
		}
	//
	__UHEAP_MARKEND;
	CLOSE_LOGGER
	return r;
	}


//=====================================================================
//CBTManMessage
//=====================================================================

// This framework is retained as it allows instrinsically asynchronous actions to be
// looked after easily.  At present such actions are limited to change notifications
// pretty much everything else is handled synchronously once the Server has had
// its ServiceL called.

CBTManMessage* CBTManMessage::NewL(const RMessage2& aMessage)
	{
	LOG_STATIC_FUNC
	CBTManMessage* m = new (ELeave) CBTManMessage(aMessage);
	CleanupStack::PushL(m);
	m->ConstructL();
	CleanupStack::Pop(m);
	return m;
	}

CBTManMessage::CBTManMessage(const RMessage2& aMessage)
: iMessage(aMessage)
	{
	LOG_FUNC
	}

void CBTManMessage::ConstructL()
	{
	LOG_FUNC
	if (iMessage.Ptr2())
		{
		TPckgBuf<TBTManClientServerMessage> msgBuf;
		iMessage.ReadL(KBTManClientServerProtocolSlot, msgBuf);
		
		iCancelPtr = msgBuf().iClientStatusToCancel;
		}
	}


CBTManMessage::~CBTManMessage()
	{
	LOG_FUNC
	//ensure we delete the helper if there is one
	if (iHelper)
		{
		iHelper->Delete();
		}
	}

void CBTManMessage::SetHelper(MBTManHelper* aHelper)
/**
Assigns a helper to the message.
The helper will then be told to delete itself when the message is completed.
This is especially important when cancelling or under leave conditions.
**/
	{
	LOG_FUNC
	__ASSERT_DEBUG(iHelper==NULL, PanicServer(EBTManBadHelper));
	__ASSERT_DEBUG((aHelper->Message()).Ptr2() == iCancelPtr, PanicServer(EBTManBadHelper));
	iHelper = aHelper;
	}

void CBTManMessage::RemoveHelper()
/**
Disassociates a helper with a message.
**/
	{
	LOG_FUNC
	iHelper = NULL;
	}

void CBTManMessage::Complete(TInt aReason)
/**
Completes the RMessage2 and deletes any associated helper.
**/
	{
	LOG_FUNC
	//complete the message
	iMessage.Complete(aReason);
	//delete the helper if there is one
	if (iHelper)
		iHelper->Delete();
	}

const RMessage2& CBTManMessage::Message()
	{
	LOG_FUNC
	return iMessage;
	}

const TAny* CBTManMessage::CancelPtr()
/**
The CancelPtr is the address of the TRequestStatus of the client-side active object
dealing with the request.  When a request is cancelled, it is located server-side using
this address.
**/
	{
	LOG_FUNC
	return iCancelPtr;
	}


MBTManHelper* CBTManMessage::Helper()
	{
	LOG_FUNC
	return iHelper;
	}

TBool CBTManMessage::operator==(CBTManMessage& aMessage) const
	{
	LOG_FUNC
	if (iMessage == aMessage.Message())
		return ETrue;
	return EFalse;
	}

//=====================================================================
//CBTManSession
//=====================================================================

CBTManServer& CBTManSession::Server()
	{
	LOG_FUNC
	return *static_cast<CBTManServer*>(const_cast<CServer2*>(CSession2::Server()));
	}

CBTManSession::CBTManSession(CBTRegistry& aRegistry, const RMessage2& aMessage)
: iRegistry(aRegistry), iCurrentMessage(aMessage)
	{
	LOG_FUNC
	}

void CBTManSession::CreateL()
/**
2nd phase construct for sessions - called by the CServer2 framework
**/
	{
	LOG_FUNC
	//CSession2::CreateL();	// private and does nowt so removed
	//Add session to server first. If anything leaves, it will be removed by the destructor
	Server().AddSession();
	ConstructL();
	}

void CBTManSession::ConstructL()
	{
	LOG_FUNC
	//Create new object index
	iSubSessions = CObjectIx::NewL();
	iContainer = Server().NewContainerL();

	iMessageArray = new(ELeave) CArrayPtrFlat<CBTManMessage>(KMessageArrayGranularity);
	}

CBTManSession::~CBTManSession()
	{
	LOG_FUNC
	
	delete iSubSessions;
	Server().DeleteContainer(iContainer);
	Server().DropSession();
	
	if (iMessageArray)
		{
		__ASSERT_DEBUG(iMessageArray->Count()== 0, PanicServer(EBTManOutstandingMessagesOnClosedSession));
	
		CompleteOutstandingMessages();
		iMessageArray->ResetAndDestroy();
		}
	delete iMessageArray;
	}

void CBTManSession::CompleteOutstandingMessages()
/**
Completes any messages left in the CBTManMessage array.
**/
	{
	LOG_FUNC
	CBTManMessage* ptr;
	TInt count = iMessageArray->Count();
	LOG1(_L("CBTManSession::CompleteOutstandingMessages(): %d"), count);
	for (TInt i=(count-1); i>=0; i--)
		{
		ptr = iMessageArray->At(i);
		ptr->Complete(KErrDied);
		iMessageArray->Delete(i);
		delete ptr;
		ptr = NULL;
		}
	}


CBTManSubSession* CBTManSession::SubSessionFromHandle(TInt aHandle)
/**
Returns a subsession given a handle

	@param aHandle the handle given by the client
	@param aMessage	only used if we need to panic client's having used a dodgy handle
**/
	{
	LOG_FUNC
	CBTManSubSession* p = static_cast<CBTManSubSession*>(iSubSessions->At(aHandle));
	return p;
	}

TInt CBTManSession::HandleError(TInt aError, const RMessage2 &aMessage)
/**
Handle an error from ServiceL()
A bad descriptor error implies a badly programmed client, so panic it;
otherwise report the error to the client
**/
	{
	LOG_FUNC
	LOG1(_L("CBTManSession::HandleError(): %d"), aError);
	if (aError==KErrBadDescriptor || aError==KErrBadHandle)
		{
#ifdef _DEBUG
		__DEBUGGER(); //******** SERVER STOP ******************
#endif
		TInt convErr = aError==KErrBadDescriptor?EBTManBadDescriptor:EBTManBadSubSessionHandle;
		PanicClient(aMessage, convErr, this);
		}
	else
		{
		CompleteMessage(aMessage, aError);
		}
	Server().ReStart();
	return KErrNone;	// handled the error fully
	}


void CBTManSession::ServiceL(const RMessage2 &aMessage)
/**
Handle a client request.
Leaving is handled by HandleError() which reports the error code
to the client
**/
	{
	LOG_FUNC		
	//We don't need to create a message object if message is handled by session
	//Simply dispatch and complete the message
	TBool handled = ETrue;

	TRAPD(err, handled = DispatchSessMessageL(aMessage));
	if (handled || (err!=KErrNone))
		{
		if (!iClientPanic)
			{
			// Don't complete messages already completed by ClientPanic
			aMessage.Complete(err);
			}
		else 
			{
			//Client Panic Handled - Reset Flag.
			iClientPanic = EFalse;
			}
		return;
		}

	//not handled by session so must be for subsession
	//Create a message object to handle this
	//We need to trap this so a leave doesn't propogate to active scheduler
	err = KErrNone;
	TRAP(err, CreateBTManMessageL(aMessage));
	if (err)
		{
		aMessage.Complete(err);
		return;
		}

	//Trap dispatchSubSessMessage so we can handle error here
	err=KErrNone;
	TRAP(err, DispatchSubSessMessageL(aMessage));
	//if we have an error, try to handle it with handleerror. If this fails, leave
	//and let the active scheduler sort it out.
	if (err != KErrNone)
		{
		// tell subsessions to try to reduce footprint and rollback
		for (TInt i = 0; i<iSubSessions->Count(); i++)
			{
			//check this element is an actual object rather than an empty memory 
			//cell on the free list
			if((*iSubSessions)[i])
				{
				static_cast<CBTManSubSession*>((*iSubSessions)[i])->Cleanup(err);
				}
			}
		// Tidy up of logic and construct
		if (err != KErrNone)
			{
			User::LeaveIfError(HandleError(err, aMessage));
			return;
			}
		}
	}

void CBTManSession::CreateBTManMessageL(const RMessage2& aMessage)
/**
Creates a CBTManMessage and appends it to iMessageArray.
These are used for the BTMan server to conduct asynchronous activities itself

Many activities that the BTMan Server does at present are synchronously handled; but
for conformity all the subsessions use this framework so that any asynchronous
tasks can be added later

	@param	aMessage	The original client message
**/
	{
	LOG_FUNC
	CBTManMessage* m = CBTManMessage::NewL(aMessage);
	CleanupStack::PushL(m);
	iMessageArray->AppendL(m);
	CleanupStack::Pop();
	}


TBool CBTManSession::DispatchSessMessageL(const RMessage2 &aMessage)
/**
Handles the request - it may not actually be for the session

@param aMessage    The current message being handled.
@return			   ETrue: The message was meant for the session.
@return            EFalse: The message was meant for a subsession.
**/
	{
	LOG_FUNC
	switch (aMessage.Function())
		{
	case EBTManCreateRegistrySubSession:
		NewSubSessionL(ERegistry, aMessage);
		return ETrue;
	case EBTManCreateLocalDeviceSubSession:
		NewSubSessionL(ELocalDevice, aMessage);
		return ETrue;
	case EBTManCreateCommPortSettingsSubSession:
		NewSubSessionL(ECommPortSettings, aMessage);
		return ETrue;
	case EBTManCreateHostResolverSubSession:
		NewSubSessionL(EHostResolver, aMessage);
		return ETrue;
	case EBTManCancelRequest:
		CancelRequest(aMessage);
		return ETrue;
	case EBTManCloseSubSession:
		CloseSubSession(aMessage);
		return ETrue;
	case EBTManSetHeapFailure:
#ifdef _DEBUG
		User::__DbgSetAllocFail(RHeap::EUser,RHeap::TAllocFail(aMessage.Int0()),aMessage.Int1());
#endif
		return ETrue;
	case EBTManSubSessionCount:
		{
#ifdef _DEBUG
		TPckgBuf<TInt> pckg(iSubSessions->Count());
		aMessage.WriteL(0, pckg);
#endif
		return ETrue;
		}
	default:
		//not handled here so must be for subsession
		return EFalse;
		}
	}

void CBTManSession::DispatchSubSessMessageL(const RMessage2& aMessage)
/**
Handles subsession requests.

	@param aMessage    The current message being handled.
**/
	{
	LOG_FUNC
	CBTManSubSession& ss = *SubSessionFromHandle(aMessage.Int3());
	if (&ss == NULL)
		{
		User::Leave(KErrBadHandle);
		}

	// Get the status of the client request (sent on all msgs in slot2)
	switch (aMessage.Function())
		{
	case EBTManRegistrySearch:
		{
		TBTRegistrySearchPckgBuf pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTRegistrySubSession*>(&ss)->OpenViewL(pckg(), aMessage);
		break;
		}
	case EBTRegistryAddDevice:
		{
		// create a new device subsession and add the device - may change API to require
		// previously opened subsession??
		// need to internalise a contiguated buffer
	
		TInt len = aMessage.GetDesLengthL(0);
		HBufC8* buffer = HBufC8::NewLC(len);
		TPtr8 ptr(buffer->Des());

		aMessage.ReadL(0, ptr);
		RDesReadStream stream;
		stream.Open(ptr);
		CleanupClosePushL(stream);

		CBTDevice* addDetails = CBTDevice::NewLC();
		addDetails->InternalizeL(stream);

		static_cast<CBTRegistrySubSession*>(&ss)->AddDeviceL(*addDetails, aMessage);

		CleanupStack::PopAndDestroy(3); //addDetails, stream, buffer
		break;
		}
	case EBTRegistryModifyNamelessDevice:
		{
		LOG(_L("CBTManSession -> ModifyNamelessDevice"));
		TBTNamelessDevicePckgBuf pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTRegistrySubSession*>(&ss)->ModifyL(pckg(), aMessage);
		break;
		}
	case EBTRegistryGetNamelessDevice:
		{
		TBTNamelessDevicePckgBuf pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTRegistrySubSession*>(&ss)->GetDeviceL(pckg(), aMessage);
		break;
		}
	case EBTManExtractRegistryDataIntoServer:
		{
		static_cast<CBTRegistrySubSession*>(&ss)->PreLoadL(aMessage);
		break;
		}

	case EBTManRetrieveRegistryData:
		{
		static_cast<CBTRegistrySubSession*>(&ss)->RetrieveL(aMessage);
		break;
		}

	case EBTRegistryDeleteLinkKey:
		{
		TBTDevAddrPckgBuf pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTRegistrySubSession*>(&ss)->UnpairL(pckg(), aMessage);
		break;
		}

	case EBTRegistryDeleteDevices:
		{
		//nothing to read
		static_cast<CBTRegistrySubSession*>(&ss)->DeleteViewL(aMessage);
		break;
		}

	case EBTRegistryModifyBluetoothName:
	case EBTRegistryModifyFriendlyName:
		{
		// read the address - this could move into the handler
		TBTDevAddrPckgBuf addr;
		aMessage.ReadL(0, addr);
		static_cast<CBTRegistrySubSession*>(&ss)->ModifyNameL(addr(), aMessage);
		break;
		}

	case EBTRegistryGetLocalDevice:
		{
		//nothing to read
		static_cast<CBTLocalDeviceSubSession*>(&ss)->GetL(aMessage);
		break;
		}

	case EBTRegistryUpdateLocalDevice:
		{
		TPckgBuf<TBTLocalDevice> pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTLocalDeviceSubSession*>(&ss)->UpdateL(pckg(), aMessage);
		break;
		}

	case EBTRegistryGetCommPortSettings:
		{
		TPckgBuf<TBTCommPortSettings> pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTCommPortSettingsSubSession*>(&ss)->GetL(pckg(), aMessage);
		break;
		}

	case EBTRegistryUpdateCommPortSettings:
		{
		TPckgBuf<TBTCommPortSettings> pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTCommPortSettingsSubSession*>(&ss)->UpdateL(pckg(), aMessage);
		break;
		}

	case EBTRegistryDeleteCommPortSettings:
		{
		TPckgBuf<TBTCommPortSettings> pckg;
		aMessage.ReadL(0, pckg);
		static_cast<CBTCommPortSettingsSubSession*>(&ss)->DeleteL(pckg(), aMessage);
		break;
		}

	case EBTRegistryUnpairView:
		{
		static_cast<CBTRegistrySubSession*>(&ss)->UnpairViewL(aMessage);
		break;
		}
		
	case EBTRegistryCloseView:
		{
		static_cast<CBTRegistrySubSession*>(&ss)->CloseView(aMessage);
		break;
		}	
	
	case EBTRegistryNotifyViewChange:
		{
		ss.SetViewChangeNotificationMessage(aMessage);
		break;
		}
		
	case EBTHostResolverDeviceRequest:
	case EBTHostResolverDeviceModifyDevice:
	default:
		PanicClient(aMessage, EBTManBadRequest, this);
		return;
		}
	}

void CBTManSession::NewSubSessionL(TSubSessionType aType, const RMessage2 &aMessage)
/**
Create a subsession of type aType and add it to the object index.
**/
	{
	LOG_FUNC
	//Make new counter object
	CBTManSubSession* ss = NULL;
	switch (aType)
		{
	case ERegistry:
		ss = CBTRegistrySubSession::NewL(*this, iRegistry);
		break;
	case ELocalDevice:
		ss = CBTLocalDeviceSubSession::NewL(*this, iRegistry);
		break;
	case ECommPortSettings:
		ss = CBTCommPortSettingsSubSession::NewL(*this, iRegistry);
		break;
	case EHostResolver:
	default:
		PanicServer(EBTManBadSubSessionType);
		}

	//add to container to generate unique id
	//If anything goes wrong, make sure we close the subsession.
	CleanupClosePushL(*ss);

	iContainer->AddL(ss);

	//add to object index - this returns a unique handle
	// From inspection of CObjectIx::AddL(), it will can only leave BEFORE ss is added to the index.
	// Therefore, we must close the subsession and leave if anything goes wrong, 
	// and NOT call CObjectIx::Remove().
	TInt handle = 0;
	handle = iSubSessions->AddL(ss);

	//ss now has an owner so we can pop it off the cleanupstack
	CleanupStack::Pop();	//ss

	//Write handle to client
	//If anything goes wrong, call CObjectIx::Remove(). This will close the subsession for us.
	TPckg<TInt> pckg(handle);
	TRAPD(err, aMessage.WriteL(3, pckg));
	if (err!=KErrNone)
		{
		iSubSessions->Remove(handle);
		User::Leave(err);
		}
	//increase resource count
	iResourceCount++;
	}

void CBTManSession::DeleteSubsession(TInt aHandle, const RMessage2 &aMessage)
	{
	LOG_FUNC
	//panic client if bad handle
	CBTManSubSession* ss = (CBTManSubSession*)iSubSessions->At(aHandle);
	if (ss == NULL)
		{
		PanicClient(aMessage, EBTManBadSubSessionRemove, this);
		//Flag that Client has been panic'd 
		//so that the message is not completed twice
		iClientPanic = ETrue;
		}
	else
		{
		iSubSessions->Remove(aHandle);
		//decrement resource count
		iResourceCount--;
		}
	}

void CBTManSession::CloseSubSession(const RMessage2 &aMessage)
	{
	LOG_FUNC
	// handle for subsession is in slot3
	DeleteSubsession(aMessage.Int3(), aMessage);
	}

void CBTManSession::CompleteMessage(const RMessage2& aMessage, TInt aReason)
/**
Finds a message based on aMessage and completes it.
**/
	{
	LOG_FUNC
	CBTManMessage* m = FindMessage(aMessage);
	__ASSERT_DEBUG(m!=NULL, PanicServer(EBTManBadBTManMessage));
	DoCompleteMessage(*m, aReason);
	}

void CBTManSession::CompleteMessage(MBTManHelper* aHelper, TInt aReason)
/**
Finds a message based on aHelper and completes it.
**/
	{
	LOG_FUNC
	CBTManMessage* m = FindMessage(aHelper);
	__ASSERT_DEBUG(m!=NULL, PanicServer(EBTManBadBTManMessage));
	DoCompleteMessage(*m, aReason);
	}

void CBTManSession::DoCompleteMessage(CBTManMessage& aMessage, TInt aReason)
/**
	Actually does the completion
**/
	{
	LOG_FUNC
	if (aMessage.Message().Ptr2())
		{
		// notify clientAPI that we've serviced it - not needed if the client has
		// not asked us to (typically for synchronous calls)
		TBTManClientServerMessage clientMsg;
		TPckg<TBTManClientServerMessage> clientMsgBuf(clientMsg);
		TRAPD(err_read, aMessage.Message().ReadL(KBTManClientServerProtocolSlot, clientMsgBuf));
		clientMsg.iClientBusy=EFalse;
		TRAPD(err_write, aMessage.Message().WriteL(KBTManClientServerProtocolSlot, clientMsgBuf));
		
		if(err_read != KErrNone || err_write != KErrNone)
			{
			PanicClient(aMessage.Message(),EBTManBadBTManMessage, this);
			return;
			}
		}
	// now tell the kernel
	aMessage.Complete(aReason);
	DeleteMessage(&aMessage);
	}

CBTManMessage* CBTManSession::FindMessage(const RMessage2& aMessage)
/**
Searches the array of CBTManMessages for the one dealing with aMessage.
**/
	{
	LOG_FUNC
	CBTManMessage* ptr;
	for (TInt i=0; i<iMessageArray->Count(); i++)
		{
		ptr = iMessageArray->At(i);
		if(ptr->Message() == aMessage)
			{
			return ptr;
			}
		}
	return NULL;
	}

CBTManMessage* CBTManSession::FindMessage(MBTManHelper* aHelper)
/**
Searches the array of CBTManMessages for the one containing a pointer to aHelper.
**/
	{
	LOG_FUNC
	CBTManMessage* ptr;
	for (TInt i=0; i<iMessageArray->Count(); i++)
		{
		ptr = iMessageArray->At(i);
		if(ptr->Helper() == aHelper)
			{
			return ptr;
			}
		}
	return NULL;
	}

void CBTManSession::DeleteMessage(CBTManMessage* aMessage)
/**
Find the CBTManMessage in the message array and delete it.
**/
	{
	LOG_FUNC
	CBTManMessage* ptr;
	TInt count = iMessageArray->Count();
	for (TInt i=(count-1); i>=0; i--)
		{
		ptr = iMessageArray->At(i);
		if(ptr == aMessage)
			{
			//Delete the message first before removing from the array since a helper associated
			//with the message will try to find the message by parsing the array as part of the
			//destruction the message.
			delete ptr;
			iMessageArray->Delete(i);
			ptr = NULL;
			break;
			}
		}
	//compress the array if the count is less than the length - granularity AND if the count != 0
	if (iMessageArray->Count())
		{
		if (iMessageArray->Length() - iMessageArray->Count() >= KMessageArrayGranularity)
			{
			iMessageArray->Compress();
			}
		}
	}

void CBTManSession::CancelRequest(const RMessage2& aMessage)
/**
Cancels an asyncronous request.
 We need to find the asynchronous message we are cancelling
 - we can do this by comparing the TRequestStatus held
 in Ptr2() of the message.  This is ok since ALL asynchronous
 requests to this server have the TRequestStatus passed over
 in Ptr2() and only these may be cancelled. The TRequestStatus
 is sent over in ptr0() of the cancel request so we don't confuse
 the cancel message with the one we are trying to cancel.
**/
	{
	LOG_FUNC
	CBTManMessage* m;
	TInt count = iMessageArray->Count();
	for (TInt i=(count-1); i>=0; i--)
		{
		m = iMessageArray->At(i);
		if (m->CancelPtr() == aMessage.Ptr0())
			{
			//Complete the message
			m->Complete(KErrCancel);
			//delete the message from the array
			iMessageArray->Delete(i);
			delete m;
			m = NULL;
			break;
			}
		}
	//aMessage will be completed by CBTManSession::ServiceL()
	}

TBool CBTManSession::SubSessionHasOverlappingView(CBTManSubSession& aSubSessionViewOwner, const TDesC& aViewDescriptor)
	{
	LOG_FUNC
	// Iterate over all subsessions apart from the view owner
	TBool overlapFound = EFalse;
	for (TInt i = 0; i < iContainer->Count(); i++)
		{
		CBTManSubSession* ss = static_cast<CBTManSubSession*>((*iContainer)[i]);
		if (&aSubSessionViewOwner != ss)
			{
			if (ss->IsOverlappingView(aViewDescriptor))
				{
				// Overlaps - subsession will have completed the Notify message
				// With bool return, we can test if indeed it did.
				overlapFound = ETrue;
				}
			}
		}

	return overlapFound;
	}

TBool CBTManSession::SubSessionHasOverlappingView(const TDesC& aViewDescriptor)
	{
	LOG_FUNC
	// Iterate over all subsessions (view owner is not a subsession)
	TBool overlapFound = EFalse;
	for (TInt i = 0; i < iContainer->Count(); i++)
		{
		CBTManSubSession* ss = static_cast<CBTManSubSession*>((*iContainer)[i]);
		if (ss->IsOverlappingView(aViewDescriptor))
			{
			// Overlaps - subsession will have completed the Notify message
			// With bool return, we can test if indeed it did.
			overlapFound = ETrue;
			}
		}

	return overlapFound;
	}


//=====================================================================
//	CBTManSubSession
//=====================================================================

CBTManSubSession::CBTManSubSession(CBTManSession& aSession, CBTRegistry& aRegistry) :
	iSession(aSession), iRegistry(aRegistry)
	{
	LOG_FUNC
	}

void CBTManSubSession::NotifyChange(TUint aTableChanged)
	{
	LOG_FUNC
	iSession.Server().Publish(KPropertyKeyBluetoothGetRegistryTableChange,
							  aTableChanged);
	}

void CBTManSubSession::NotifyChange(TUint aTableChanged, CBTManSubSession& aSubSessionViewOwner, const TDesC& aViewDescriptor)
	{
	LOG_FUNC
	NotifyChange(aTableChanged);
	iSession.Server().NotifyViewChange(aSubSessionViewOwner, aViewDescriptor);
	}	

/*virtual*/ TBool CBTManSubSession::IsOverlappingView(const TDesC& /*aViewDescriptor*/)
	{
	LOG_FUNC
	// By default, not supported - so no overlapping view.
	return EFalse;
	}

/*virtual*/ void CBTManSubSession::SetViewChangeNotificationMessage(const RMessage2& aMessage)
	{
	LOG_FUNC
	// By default, not supported
	iSession.CompleteMessage(aMessage, KErrNotSupported);
	}

#ifdef __BTMANSERVER_NO_PROCESSES__

// The server binary is an "EPOCEXE" target type
// Thus the server parameter passing and startup code for WINS and EPOC are
// significantly different.
//
// In EKA1 WINS, the EPOCEXE target is a DLL with an entry point called WinsMain,
// taking no parameters and returning TInt. This is not really valid as a thread
// function which takes a TAny* parameter which we need.
//
// So the DLL entry-point WinsMain() is used to return a TInt representing the
// real thread function within the DLL. This is good as long as
// sizeof(TInt)>=sizeof(TThreadFunction).
//

static TInt ThreadFunction(TAny*)
//
// WINS thread entry-point function.
//
	{
	LOG_FUNC
	return E32Main();
	}

IMPORT_C TInt WinsMain();
EXPORT_C TInt WinsMain()
//
// WINS DLL entry-point. Just return the real thread function 
// cast to TInt
//
	{
	LOG_FUNC
	return reinterpret_cast<TInt>(&ThreadFunction);
	}

TInt E32Dll(TDllReason)
	{
	LOG_FUNC
	return KErrNone;
	}

#endif