// Copyright (c) 1997-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:
// BTComm state machine state implementation.
// 
//

#include <bluetooth/logger.h>
#include <cs_port.h>
#include "btcomm.h"
#include "btstate.h"
#include "btcommactive.h"
#include <bt_sock.h>
#include "sdpkey.h"
#include "sdpclientcsy.h"
#include <e32test.h>
#include <btmanclient.h>
#include "btcommutil.h"

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

#define BAD_BTCOMM_EVENT PanicInState(EBTCommBadEventForThisState);



TBTPortState::TBTPortState(CBTPortStateFactory* aParent)
	: iFactory(aParent)
/**
	TBTPortState c'tor.
	TBTPortState is the abstract base class that all CSY state
	objects inherit from.  However, it is not a 'C' class but 
	a 'T' class which is slightly bogus but the best way to 
	implement the base state for the pattern.
**/
	{
	LOG_FUNC
	}

TBTPortState::~TBTPortState()
	{
	LOG_FUNC
	}

/**
Calls the appropriate panic function to encode the panic
code with the current state identifier.
@param aPanic The panic code that the state is panicking with.
*/
void TBTPortState::PanicInState(TBTCommPanic aPanic) const
	{
	LOG_FUNC
	BTCommUtil::Panic(aPanic, iFactory->StateIndex(this));
	}

//		***** Default state *****

TBTPortDefaultState::TBTPortDefaultState(CBTPortStateFactory* aParent) 
	: TBTPortState(aParent)
	{
	LOG_FUNC
	}

TBTPortDefaultState::~TBTPortDefaultState()
	{
	LOG_FUNC
	}

void TBTPortDefaultState::Open(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::Close(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::Read(CBTPortProxy* /*aContext*/,TAny* /*aPtr*/,TInt /*aLength*/)
/**
	TBTPortDefaultState Read.
**/
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::Write(CBTPortProxy* /*aContext*/,TAny* /*aPtr*/,TInt /*aLength*/)
/**
	TBTPortDefaultState Write.
**/
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::ReadCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::WriteCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::DoRunL(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::DoCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::DoLockedAction(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::DoWriteCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortDefaultState::DoReadCompleted(CBTPortProxy* /*aContext*/,TInt aError) 
	{ 
	// because we managed to get reopened before destruction 
	// and this event sources from the reader's request completing 
	// after a read cancel was issued from the closing. 
	LOG_FUNC
	if(aError==KErrCancel) {return;}  

	BAD_BTCOMM_EVENT 
	} 


void TBTPortDefaultState::Error(CBTPortProxy* aContext,TInt aError)
/**
	This will call each state's error loging and move the CSY to the error state.
*/
	{
	LOG_FUNC
	LogStateError(aContext,aError);
	aContext->iLastError=aError; // record this before we lose context
	aContext->MoveToErrorState();
	}


// ****** Common Pre-Connection I/O Base State  *****

TBTPortCommonBaseState::TBTPortCommonBaseState(CBTPortStateFactory* aParent)
	:TBTPortDefaultState(aParent)
	{
	LOG_FUNC
	}

void TBTPortCommonBaseState::Read(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
	{
	LOG_FUNC

	if(aLength)
		{
		aContext->iClientReadPtr=aPtr;
		aContext->iClientReadLength=aLength;
		aContext->iClientRemainderToRead=aLength;
#ifdef _DEBUG
	aContext->iReadsPending++;
#endif
		}
	else
		{
		//zero length read, complete it immediately
		aContext->iPort->ReadCompleted(KErrNone);
		}
	}

void TBTPortCommonBaseState::Write(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
	{
	LOG_FUNC

	aContext->iClientWritePtr=aPtr;
	aContext->iClientWriteLength=aLength;
	aContext->iClientLengthWrittenSoFar=aLength;
	}

void TBTPortCommonBaseState::WriteCancel(CBTPortProxy* aContext)
/**
	Cancel initial Write if there was one or do nothing.
*/
	{
	LOG_FUNC

	if(aContext->iClientWritePtr)
		{
		aContext->iClientWritePtr=NULL;
		aContext->iClientWriteLength=0;
		aContext->iClientLengthWrittenSoFar=0;
		aContext->iPort->WriteCompleted(KErrCancel);
		}
	}

void TBTPortCommonBaseState::ReadCancel(CBTPortProxy* aContext)
/**
	Cancel initial Read if there was one or do nothing.
*/
	{
	LOG_FUNC

	if(aContext->iClientReadPtr)
		{
		aContext->iClientReadPtr=NULL;
		aContext->iClientReadLength=0;
		aContext->iClientRemainderToRead=0;
#ifdef _DEBUG
		aContext->iReadsPending--;
#endif
		aContext->iPort->ReadCompleted(KErrCancel);
		}
	}

void TBTPortCommonBaseState::Close(CBTPortProxy* aContext)
	{
	LOG_FUNC

	aContext->StopReader();

	//check to see if we had any client requests cached and cancel them
	if(aContext->iClientReadPtr)
		{
		ReadCancel(aContext);
		}	
	if(aContext->iClientWritePtr)
		{
		WriteCancel(aContext);
		}

	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
	}

//		***** Idle state *****

TBTPortStateIdle::TBTPortStateIdle(CBTPortStateFactory* aParent)
	:TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateIdle::~TBTPortStateIdle()
	{
	LOG_FUNC
	}

void TBTPortStateIdle::Open(CBTPortProxy* aContext)
/**
	This function is the entry point for the BT CSY state machine.
	The first thing we will do is to pretend we opened a BTCOMM connection.
	The reason for this is to avoid any deadlock across the C32-ESock-ETel 
	trilogy.
  **/
	{
	LOG_FUNC
	aContext->CancelShutDownTimer(); // just in case we were going down before this call
	}

void TBTPortStateIdle::Close(CBTPortProxy* aContext)
/**
	A call to this method will cancel any pending requests.
	It will also cancel any transition to any other state.
  */	
	{
	LOG_FUNC
	
	aContext->Cancel();// any transition

	//check to see if we had any client requests cached and cancel them
	ReadCancel(aContext);	
	WriteCancel(aContext);
	}


void TBTPortStateIdle::Read(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
/**
	Caches the Read() for completion after the connection is setup, starts the protocol and state machine.
*/
	{
	LOG_FUNC
	LOG(_L("**TBTPortStateIdle::Read -- Queueing one**"));
	TBTPortCommonBaseState::Read(aContext,aPtr,aLength);
	// Connect to ESock
	SockServConnect(aContext);
	}

void TBTPortStateIdle::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
/**
	Caches the Write() for completion after the connection is setup, starts the protocol and state machine.
*/
	{
	LOG_FUNC
	LOG(_L("**TBTPortStateIdle::Write -- Queueing one**"));
	TBTPortCommonBaseState::Write(aContext,aPtr,aLength);
	// Connect to ESock
	SockServConnect(aContext);
	}


void TBTPortStateIdle::DoRunL(CBTPortProxy* /*aContext*/)
/**
	Will be called only after we  decided to close in the Discovering state.
	This is a NoOp method.
 */
	{
	LOG_FUNC
	}

void TBTPortStateIdle::SockServConnect(CBTPortProxy* aContext)
/**
	Makes an asynchronous attempt to connect to ESock and moves to the next state.
**/
	{
	LOG_FUNC
	aContext->iSockServConnector->SockServConnect(aContext->iStatus);
	
	// now move to the next state where we pre-load the protocol
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::ELoadingProtocol));
	aContext->SetActive();
	}

void TBTPortStateIdle::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}
	
//		***** Loading Protocol state *****

TBTPortStateLoadingProtocol::TBTPortStateLoadingProtocol(CBTPortStateFactory* aParent)
	:TBTPortCommonBaseState(aParent), iClosePending(EFalse)
	{
	LOG_FUNC
	}

TBTPortStateLoadingProtocol::~TBTPortStateLoadingProtocol()
	{
	LOG_FUNC
	}

void TBTPortStateLoadingProtocol::Read(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
	{
	LOG_FUNC

	// Cache the read ready for when our connection is up. 
	if(aLength)
		{
		aContext->iClientReadPtr=aPtr;
		aContext->iClientReadLength=aLength;
		aContext->iClientRemainderToRead=aLength;
#ifdef _DEBUG
	aContext->iReadsPending++;
#endif
		}
	else
		{
		//zero length read, complete it immediately
		aContext->iPort->ReadCompleted(KErrNone);
		}

	// If we were planning on closing this reopens us - we've cached the read as
	// normal, just unset the close pending flag and we can carry on setting up
	// the connection as normal.
	if(!iClosePending)
		{
		iClosePending = EFalse;
		}
	}

void TBTPortStateLoadingProtocol::Write(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
	{
	LOG_FUNC
	
	// Cache the write ready for when our connection is up.  
	aContext->iClientWritePtr=aPtr;
	aContext->iClientWriteLength=aLength;
	aContext->iClientLengthWrittenSoFar=aLength;
	
	// If we were planning on closing this reopens us - we've cached the write as
	// normal, just unset the close pending flag and we can carry on setting up
	// the connection as normal.
	if(iClosePending)
		{
		iClosePending = EFalse;
		}
	}

void TBTPortStateLoadingProtocol::Close(CBTPortProxy* aContext)
/**
	A call to this method will cancel any pending requests.
	It will also cancel any transition to any other state.
  */	
	{
	LOG_FUNC
	
	// If we're in this state our connection attempt on ESock hasn't
	// completed yet.  We can't transition to the closing state yet 
	// because we don't have the handle for our ESock session which 
	// we need to close.  When the connection request completes we'll 
	// do the transition, unless there's a Read or Write before then, 
	// when we'll allow the connection set up to continue.
	
	//check to see if we had any client requests cached and cancel them
	ReadCancel(aContext);	
	WriteCancel(aContext);
	
	// Set the flag so we know where to go when the Esock connect has
	// completed
	iClosePending = ETrue;
	}

void TBTPortStateLoadingProtocol::DoRunL(CBTPortProxy* aContext)
/**
	Will be called only after we  decided to close in the Discovering state.
	This is a NoOp method.
 */
	{
	LOG_FUNC
	
	if (aContext->iStatus != KErrNone)
		{
		Error(aContext, aContext->iStatus.Int());	// couldn't connect to esock
		return;
		}
		
	// If we're waiting for the ESock handle to allow us to close, transition
	// to the closing state now, otherwise continue connection set up.
	if(iClosePending)
		{
		aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
		aContext->SetActive();
		TRequestStatus* pStatus = &(aContext->iStatus);
		User::RequestComplete(pStatus, KErrNone);
		
		// Reset this incase we come back through the state machine later
		iClosePending = EFalse;
		}
	else
		{
		StartProtocol(aContext);
		}
	}

void TBTPortStateLoadingProtocol::StartProtocol(CBTPortProxy* aContext)
/**
	Makes an asynchronous attempt to invoke RSocketServ::StartProtocol() and moves to the next state.
**/
	{
	LOG_FUNC
    // now async load Bluetooth RFComm protocol to stop comm port open thread lock.
	RSocketServ &sockServ = aContext->iSockServ;
	LOG1(_L("TBTPortStateLoadingProtocol::Start L2CAP Protocol OK, iSockServ=0x%x"),sockServ.Handle());
    sockServ.StartProtocol(KBTAddrFamily,KSockSeqPacket,KL2CAP,aContext->iStatus);
	// now move to the next state were the results will be handled
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EDiscovering));
	aContext->SetActive();
	}

void TBTPortStateLoadingProtocol::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}

//		***** Discovering state *****

TBTPortStateDiscovering::TBTPortStateDiscovering(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateDiscovering::~TBTPortStateDiscovering()
	{
	LOG_FUNC
	}


void TBTPortStateDiscovering::DoRunL(CBTPortProxy* aContext)
/**
	Find-out the port's corresponding device and settings.
	This is done by looking at the registry default settings for this 
	BTComm port. 
**/
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// could not start Bluetooth protocol 
		// must error here so that the user notices this
		Error(aContext,ret);
		LOG(_L(" Could not load Bluetooth protocol !"));		
		// do tidy up here
		return;
		}
	else
		{// we have successfully started Bluetooth, lets discover the settings for this port
		aContext->iDefaultService.SetPort(aContext->iPortNo);
		ret=aContext->iPortSettings.Get(aContext->iDefaultService); 
		
		if(ret!=KErrNone)
			{
			LOG(_L(" Could not find default device !"));		
			Error(aContext,ret);
			return;
			}
		aContext->iBdaddr=aContext->iDefaultService.BDAddr();
				
		// In the context of RFCOMM, at this stage, we would be looking
		// to do an SDP service search query on the remote RFCOMM service
		// which means opening an SDP sap first which is a synchronous 
		// action so has to be done as a locked action.
		aContext->StartLocker();
		}
	}

void TBTPortStateDiscovering::DoLockedAction(CBTPortProxy* aContext)
/**
	Do an SDP query for the remote service port number as a synchronous (locked) action.
	At this point we have locked the session.  We now need to open
	our NetDB and then follow that with our async state change action 
	which is an SDP query.
**/
	{
	LOG_FUNC
	// need to open the netdb, fire off an async SDP Connect Query
	// and then do the state transition.
	RNetDatabase& netdb=aContext->iNetDatabase;
	RSocketServ ss=aContext->iSockServ;
	// note this needs to be a reference.
	TInt ret=netdb.Open(ss,KBTAddrFamily,KSDP);
	// sync action completed ok - now stop locker.
	aContext->StopLocker();
	if (ret!=KErrNone)	
		{
		Error(aContext,ret);
		return;
		}

	// if NetDB was opened ok make a note of it for cleanup on closing state
	aContext->SetNetDbInUse();
	
	// now queue async action and move into next state
	// which is an SDP connect query on remote RFCOMM service.
	TSDPConnectQuery connQ;
	connQ.iQueryType = KSDPConnectQuery;
	connQ.iAddr = aContext->iBdaddr;
	aContext->iSDPRequest.Copy(TSDPConnectBuf(connQ));
	aContext->iSDPResult.SetMax();
	netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
	aContext->SetActive();
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPConnected));
	}

void TBTPortStateDiscovering::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
/**
	TBTPortStateDiscovering Error.
	Reaching this error means that we haven't found a device.
**/
	{
	LOG_FUNC
	}

//		***** SDP Connected state *****

TBTPortStateSDPConnected::TBTPortStateSDPConnected(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateSDPConnected::~TBTPortStateSDPConnected()
	{
	LOG_FUNC
	}

void TBTPortStateSDPConnected::DoRunL(CBTPortProxy* aContext)
/**
	Entry at this call signals that the SDP connection query has completed.  
	We now need to check the remote port number to connect to before 
	invoking the locker to handle the opening of the port proxy's socket.
**/
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// didn't manage to succeed with SDP Connect query.
		Error(aContext,ret);
		LOG(_L("CSY: Could not connect to remote SDP server !"));
		return;
		}
	// we have successfully found the remote SDP server.
	RNetDatabase& netdb=aContext->iNetDatabase;
		
	// here we create the SDP service search query. 
	TSDPServiceSearchKey key;
	key.iQueryType = KSDPServiceQuery;
	key.iMaxCount = 20;
	key.iUUID = aContext->iDefaultService.UUID();

	key.iStateLength = 0;

	aContext->iSDPRequest.Copy(TSDPServiceSearchKeyBuf(key));
	aContext->iSDPServRecordHandle.SetMax(); 
	netdb.Query(aContext->iSDPRequest,aContext->iSDPServRecordHandle,aContext->iStatus);
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPServiceQuery));
	aContext->SetActive();		
	}

void TBTPortStateSDPConnected::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
/**
	Reaching this error means that we haven't found a remote device
	with the right service level on it.  ie. the SDP/IAS query failed.
**/
	{
	LOG_FUNC
	}


//      ***** SDP Service Retrieved State *****

TBTPortStateSDPServiceQuery::TBTPortStateSDPServiceQuery(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateSDPServiceQuery::~TBTPortStateSDPServiceQuery()
	{
	LOG_FUNC
	}

void TBTPortStateSDPServiceQuery::DoRunL(CBTPortProxy* aContext)
	{
	LOG_FUNC
	// we have completed the SDP service search query
	// get number of record handles in this response
	TInt ret=aContext->iStatus.Int();

	if (ret!=KErrNone)
		{// could not start Bluetooth protocol 
		// must error here so that the user notices this
		Error(aContext,ret);
		LOG(_L("SDPService query - error."));		
		// do tidy up here
		return;
		}
	else
		{
		
		iFactory->iSDPServRecordHandleCount=BigEndian::Get16(&aContext->iSDPServRecordHandle[2]);
		if (!iFactory->iSDPServRecordHandleCount)
			{// this is an error - found no matching service records.
			Error(aContext,KErrNotFound);
			return;
			}
		if (iFactory->iSDPServRecordHandleCount*4 > aContext->iSDPServRecordHandle.Length() - 5)
			{// this is a check on the length of the response.			
			Error(aContext,KErrUnknown);
			LOG2(_L("Bad length field %d on results length %d"), 
				iFactory->iSDPServRecordHandleCount*4,aContext->iSDPServRecordHandle.Length());
			return;
			}
			
		LOG(_L("\nFound Service records for UUID 0x03\n")); //add the number of records found
			
		//now retrieve the ServiceClassIDList from the first ServiceRecordHandle
		RNetDatabase& netdb=aContext->iNetDatabase;

		TSDPAttributeKey key;
		key.iQueryType = KSDPAttributeQuery;
		// suck out the first service record handle.
		iFactory->iExtractedHandleCount = 1;
		key.iServiceRecordHandle = BigEndian::Get32(&aContext->iSDPServRecordHandle[4]);
		key.iMaxLength = 200;
		key.iRange = EFalse;
		key.iAttribute = KSdpAttrIdServiceClassIDList; 
		key.iStateLength = 0;
		aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
		aContext->iSDPResult.SetMax();
		// Now go away and do the SDP attribute request query
		netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
		aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPServiceIDListRetrieved));		
		aContext->SetActive();
		}
	}

void TBTPortStateSDPServiceQuery::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
/**
	Reaching this error means that we haven't found a remote device
	with the right service level on it.  ie. the SDP query failed.
**/
	{
	LOG_FUNC
	}



//      ***** SDP Service ID List Retrieved State *****

TBTPortStateServiceIDListRetrieved::TBTPortStateServiceIDListRetrieved(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}


TBTPortStateServiceIDListRetrieved::~TBTPortStateServiceIDListRetrieved()
	{
	LOG_FUNC
	}

void TBTPortStateServiceIDListRetrieved::DoRunL(CBTPortProxy* aContext)
	{
	LOG_FUNC

	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// could not start Bluetooth protocol 
		// must error here so that the user notices this
		Error(aContext,ret);
		LOG(_L("Service ID List error."));		
		// do tidy up here
		return;
		}
	else
		{
		RNetDatabase& netdb=aContext->iNetDatabase;

		const TInt KRspAttributeCountSize = 2;
		const TInt KContStateHeader = 1;

		LOG(_L("Successful Attribute query!!\r\nHere's the result....\n"));
		FTRACE(FHex(aContext->iSDPResult));

		if (aContext->iSDPResult.Length() < KRspAttributeCountSize+KContStateHeader)
			{
			Error(aContext,KErrNotSupported);
			LOG1(_L("Result is far too short at %d bytes\r\n"),aContext->iSDPResult.Length());
			return;
			}
		// suck out the byte count
		TUint16 bytecount=BigEndian::Get16(&aContext->iSDPResult[0]);
		if (aContext->iSDPResult.Length() < KRspAttributeCountSize+bytecount+KContStateHeader)
			{
			Error(aContext,KErrNotSupported);
			return;
			}
		if (aContext->iSDPResult[KRspAttributeCountSize+bytecount]!=0)
			{// continuation state to deal with
			// fixme - do something here!
			Error(aContext,KErrNotSupported);
			LOG(_L("Continuation state to deal with in SDP attr query response!!\r\n"));
			return;
			}

		//check for the expected UUID in the ServiceIDList

		CRFCommClass* builder=CRFCommClass::NewL(aContext->iDefaultService.UUID());
		CleanupStack::PushL(builder);
		CElementParser* parser = CElementParser::NewL(builder);
		CleanupStack::PushL(parser);
		//Parsing simple attribute list
		TRAPD(err, parser->ParseElementsL(aContext->iSDPResult.Mid(KRspAttributeCountSize,bytecount)));
		if (err)
			{
			LOG1(_L("Parser left with error %d\n"),err);
			Error(aContext,err);
			return;
			}
			
		TBool getProtocolDesc = builder->InService();
		CleanupStack::PopAndDestroy(2);

		TSDPAttributeKey key;
		key.iMaxLength = 200;
		key.iRange = EFalse;
		key.iStateLength = 0;
		key.iQueryType = KSDPAttributeQuery;
		
		aContext->iSDPResult.Zero();
		aContext->iSDPResult.SetMax();

		if (getProtocolDesc)
			{
			LOG(_L("**UUID found in ServiceClassIDList, moving on to getting Protocol Desc **"));


			// suck out the next service record handle.
			//if there are still some Handles left
			key.iServiceRecordHandle = BigEndian::Get32(&aContext->
												iSDPServRecordHandle[4*iFactory->iExtractedHandleCount]); //this handle
			key.iAttribute = KSdpAttrIdProtocolDescriptorList; 
			aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
			// Now go away and do the SDP attribute request query
			// i.e search for the first service record, to find what protocols are supported
			netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
			aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPAttribListRetrieved));		
			aContext->SetActive();
			}
		else if ( iFactory->iExtractedHandleCount < (iFactory->iSDPServRecordHandleCount) )
			{
			LOG(_L("**UUID not found in ServiceClassIDList, inquire of next ServiceRecordHandle  **"));

			// suck out the next service record handle.
			//if there are still some Handles left
			iFactory->iExtractedHandleCount++;
			key.iServiceRecordHandle = BigEndian::Get32(&aContext->
												iSDPServRecordHandle[4*iFactory->iExtractedHandleCount]); //next handle
			key.iAttribute = KSdpAttrIdServiceClassIDList; 
			aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
			// Now go away and do the SDP attribute request query
			netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
			//state is remaining unchanged so no need to call aContext->SetState()
			aContext->SetActive();
			
			}
		else //UUID has not been found & there are no more ServiceRecordHandles 
			{
			LOG(_L("**UUID not found and no ServiceRecordHandles left **"));
			Error(aContext,KErrNotFound);
			}
		}
	}


void TBTPortStateServiceIDListRetrieved::LogStateError(CBTPortProxy* /*aContext*/, TInt /*aError*/)
	{
	LOG_FUNC
	}


//      ***** SDP Attribute List Retrieved State *****

TBTPortStateSDPAttributeListRetrieved::TBTPortStateSDPAttributeListRetrieved(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateSDPAttributeListRetrieved::~TBTPortStateSDPAttributeListRetrieved()
	{
	LOG_FUNC
	}


void TBTPortStateSDPAttributeListRetrieved::DoRunL(CBTPortProxy* aContext)
/**
	The SDP attribute request query completed and here we parse the results.
*/
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// could not start Bluetooth protocol 
		// must error here so that the user notices this
		Error(aContext,ret);
		LOG(_L("AttributeListRetrieved - error."));		
		// do tidy up here
		return;
		}
	else
		{	
		const TInt KContStateHeader = 1;
		const TInt KRspAttributeCountSize = 2;

		if (aContext->iSDPResult.Length() < KRspAttributeCountSize+KContStateHeader)
			{
			Error(aContext,KErrNotSupported);
			LOG1(_L("Result is far too short at %d bytes\r\n"),aContext->iSDPResult.Length());
			return;
			}
		// suck out the byte count
		TUint16 bytecount=BigEndian::Get16(&aContext->iSDPResult[0]);
		if (aContext->iSDPResult.Length() < KRspAttributeCountSize+bytecount+KContStateHeader)
			{
			Error(aContext,KErrNotSupported);
			return;
			}
		if (aContext->iSDPResult[KRspAttributeCountSize+bytecount]!=0)
			{// continuation state to deal with
			// fixme - do something here!
			Error(aContext,KErrNotSupported);
			LOG(_L("Continuation state to deal with in SDP attr query response!!\r\n"));
			return;
			}
		LOG(_L("Successful Attribute query!!\r\nHere's the result....\n"));
		FTRACE(FHex(aContext->iSDPResult));
				
		// Parse the attribute list
				
		CRFCommAttribs* builder=CRFCommAttribs::NewL(NULL);
		CleanupStack::PushL(builder);
		CElementParser* parser = CElementParser::NewL(builder);
		CleanupStack::PushL(parser);
		//Parsing simple attribute list
		TUint rem=0;

		TRAPD(err,rem=parser->ParseElementsL(aContext->iSDPResult.Mid(KRspAttributeCountSize,bytecount)));
		if (err)
			{
			LOG1(_L("Parser left with error %d\n"),err);
			Error(aContext,err);
			// cleanup since the leave/error does not get propagated and we return.
			CleanupStack::PopAndDestroy(2);
			return;
			}
		else
			{
			LOG1(_L("Parser returned %d\n"),rem);
			}
			
		(void)(rem != KMaxTUint); // keep the compiler happy by taking rem as an r-value in urel
				
		// get the remote rfcomm port number to connect to.

		err = builder->GetRFCommPort(aContext->iRemoteRfcommPortNumber); // channel number
		if (err != KErrNone)
			{
			LOG1(_L("Error %d determining RFCOMM server channel\n"), err);
			Error(aContext,err);
			}

		CleanupStack::PopAndDestroy(2);  // getting rid of parser and builder.

		// do an RFCOMM connect on the remote RFCOMM port
		// which means opening the socket first which is a synchronous 
		// action so has to be done as a locked action.
		aContext->StartLocker();
		}
	}

void TBTPortStateSDPAttributeListRetrieved::DoLockedAction(CBTPortProxy* aContext)
/**
	Get a locked connection to the Socket server/RFComm and attempt a connection to the remote RFComm port.
**/
	{
	LOG_FUNC
	
	TPckgBuf<TUint8> aSig;
	RSocket& sock=aContext->iSocket;
	RSocketServ ss=aContext->iSockServ;
	// note this needs to be a reference.
	TInt ret=sock.Open(ss,KBTAddrFamily,KSockStream,KRFCOMM);
	if (ret!=KErrNone)
		{
		Error(aContext,ret);
		LOG(_L("**TBTPortStateSDPAttributeListRetrieved could NOT open RFComm socket connection **"));
		return;
		}
	aSig = KModemSignalDV; // KModemSignalRTC and KModemSignalRTR have been turned off
	sock.SetOpt(KRFCOMMErrOnMSC, KSolBtRFCOMM, aSig);
	// sync action completed ok - now stop locker.
	aContext->StopLocker();
	// now queue async action and move into next state
	// which is an RFComm connect on remote port.
	aContext->iAddr.SetBTAddr(aContext->iBdaddr);
	aContext->iAddr.SetPort(aContext->iRemoteRfcommPortNumber);

	TBTServiceSecurity security;
	security.SetAuthentication(aContext->iDefaultService.IsSecuritySet()); // note: does this need to be updated to be SSP aware?

	TBool doEncrypt = aContext->iDefaultService.IsEncryptionSet();
	security.SetEncryption(doEncrypt);

	aContext->iAddr.SetSecurity(security);

	sock.Connect(aContext->iAddr, aContext->iStatus);
	aContext->SetActive();	
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EConnecting));
	}


void TBTPortStateSDPAttributeListRetrieved::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
/**
	Signals a problem with the retrieval of the remote SDP attribute List.
**/
	{
	LOG_FUNC
	}


//		***** Connecting state *****

TBTPortStateConnecting::TBTPortStateConnecting(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateConnecting::~TBTPortStateConnecting()
	{
	LOG_FUNC
	}

void TBTPortStateConnecting::DoRunL(CBTPortProxy* aContext)
/**
	At this point we have finally got our connection response.
	If successful then we are in the open state and can begin
	to freely handle reads and writes.  The only state transition 
	that can take us away from the open state is a close.
**/
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// didn't manage to succeed with connect.
		Error(aContext,ret);
		LOG(_L("Didn't manage to succeed with connect"));
		return;
		}
	else
		{// we have successfully connected!!!!
		// so we can transition to the open state.
		// first we check if we have any queued writes
		// to do and we also queue a read.
		if (aContext->iClientWritePtr)
			{
			if (aContext->iClientWriteLength>KBTCOMMSendBufferLength)
				{
				aContext->iClientLengthWrittenSoFar=KBTCOMMSendBufferLength;
				aContext->iMoreSendsToCome=ETrue;
				}
			//else see TBTPortStateConnecting::Write(..)

			TPtr8& sendptr=aContext->iSendBufPtr;
			sendptr.SetLength(0);
			TPtr8 ptr((TUint8*)sendptr.Ptr(),0,aContext->iClientLengthWrittenSoFar);
			if(aContext->iClientLengthWrittenSoFar>0)
				{ // to avoid panicing on zero length write to from client
				aContext->iPort->IPCRead(aContext->iClientWritePtr,ptr,0);
				}
			sendptr.Append(ptr);
			aContext->iSendBufPtr.SetLength(aContext->iClientLengthWrittenSoFar);
			aContext->StartWriter();
			}

		if((aContext->iClientReadLength==0)&&(aContext->iClientReadPtr))
			{// someone asked for a zero length read
			aContext->iPort->ReadCompleted(KErrNone);
			}
		aContext->StartReader();
		aContext->SetState(iFactory->GetState(CBTPortStateFactory::EOpen));
		}
	}

void TBTPortStateConnecting::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
/**
	TBTPortStateConnecting Error.
	At this point our RFComm connect has failed for some reason.
**/
	{
	LOG_FUNC
	}

//		***** Open state *****

TBTPortStateOpen::TBTPortStateOpen(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent) 
	{
	LOG_FUNC
	}

TBTPortStateOpen::~TBTPortStateOpen()
	{
	LOG_FUNC
	}

void TBTPortStateOpen::Close(CBTPortProxy* aContext)
/**
	TBTPortStateOpen Close.
	This function is called when the user has invoked a Close on
	the CSY session.
	It will stop the reader and begin the shutdown and close of the socket
	session.
**/
	{
	LOG_FUNC
	
	aContext->StopReader();

	// the close() call is a direct call to C32 from the client (in ESock probably)
	// thus can pre-empt both DoLockedAction() and DoRunL(). Consequently
	// if someone issued a WriteCancel(), just before the call
	// this needs to be serviced within a DoLockedAction. But since Shutdown
	// starts here, this will not be needed. Therefore we will NOT stop the locker
	// here and will check wether a cancel is pending in the Closing state's
	// DoLockedAction().

	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
	aContext->SetActive();
	TRequestStatus* pStatus = &(aContext->iStatus);
	User::RequestComplete(pStatus, KErrNone); 
	}

void TBTPortStateOpen::Read(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
/**
	The client side has requested that a read call be completed.  
	Moreover if the reader (for the socket reads) was stoped it 
	will be restarted from here if the low watermark for the CBTPortProxy
	read-in circular buffer was reached.
**/
	{
	LOG_FUNC
#ifdef _DEBUG
	aContext->iReadsPending++;
#endif

	aContext->iClientReadPtr=aPtr;
	aContext->iClientReadLength=aLength;
	aContext->iClientRemainderToRead=aLength;
	aContext->iClientWriteOffset=0; // to indicate the offset in the buf 
									// for the first write back to client
	if(aLength)
		{
		// need to check if there's anything in the circular buffer.
		HandleIPCWriteToClient(aContext);
		}
	else
		{
		aContext->iPort->ReadCompleted(KErrNone);
		}
	// unfortunately the state has to decide whether the reader should be 
	// kicked into action again or not, because there are states like the 
	// Idle one where this is irrelevant.
	if(aContext->ReadInBufferLowWatermarkReached())
		{
		// and the reader was previously stoped
		if(!aContext->iPortReader->IsReading())
			{
			aContext->StartReader();
			}
		}
	}

void TBTPortStateOpen::DoReadCompleted(CBTPortProxy* aContext,TInt aError)
/**
	New data arrived from the socket to C32	
	This method is called by the CBTportProxy when new data arrive 
	from the socket.

	This function first needs to see if there's anything that needs to 
	be written client side.  HandleIPCWriteToClient() does the actual 
	work of invoking the c32 IPCWrite.  

	Then it checks to see if more reads to the socket should be queued or
	whether the CBTPortProxy read-in buffer is filling up.
**/
	{
	LOG_FUNC
#ifdef _DEBUG
	aContext->iReadsPending--;
#endif

	if ((aError!=KErrNone)&&(aError!=KErrCancel))
		{// we need to break out of here without requeueing the reader.
		aContext->iPort->ReadCompleted(aError);
		return;
		}
	if (aContext->iClientReadPtr)
		{// we have a read queued - 
		// need to see if we can do IPCWrite client side
		HandleIPCWriteToClient(aContext);
		}

	// check to see if we've reached or surpased our watermark
	if(aContext->ReadInBufferHighWatermarkReached())
		{
		aContext->StopReader();
		return;
		} 
	// else
	aContext->StartReader();

	}

void TBTPortStateOpen::HandleIPCWriteToClient(CBTPortProxy* aContext)
/**
	This function works out how much of the circular buffer to write back to the client. 
	The c32 ReadCompleted() function is only invoked when the complete read has been done.
**/
	{
	LOG_FUNC
	TInt dataLenInBuf=aContext->iCircularReadBuf->Count();
	if (dataLenInBuf==0)
		{
		return; //nothing to do
		}

	TInt bytesToCopy=0;
	TPtr8& readptr=aContext->iReadBufPtr; // incoming data buffer
	readptr.SetLength(0);
	TUint8* ptr=CONST_CAST(TUint8*,(readptr.Ptr()));
	TInt clientMaxLen=aContext->iClientReadLength;

	// This should cover terminated reads, plain reads and some read one 
	// or more cases		
	if(dataLenInBuf>=clientMaxLen)		
		{
		// complete if possible any terminated reads for both larger or smaller 
		// client buffers than ours
		if(aContext->iTerminatedReads)
			{ // try to find the terminated string
			// bytesToCopy here can be greater than the clientMaxLen
			// or can be -1 so we have to check
			bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
			if((bytesToCopy>clientMaxLen) || (bytesToCopy == KErrNotFound))
				{
				bytesToCopy=clientMaxLen;
				}
			// else the terminated reads complete with no terminator because 
			// the client buffer can be filled completely.
			}
		else
			{
			// The plain reads where the client has a smaller buffer should 
			// complete here for the same reason.
			// Read one or more would complete anyway for this case.
			bytesToCopy=clientMaxLen;
			}
		}
	// This should cover Receive one or more for the rest of the cases

	//will allow the full coverage of the high watermark case even if we do not do bounded reads
//#define BTCOMM_TEST_HIGH_WATERMARK
#ifndef BTCOMM_TEST_HIGH_WATERMARK
	else if(aContext->iClientReadOneOrMore)
		{
		// if we reached this point it means that datLenInBuf<clientMaxLen
		bytesToCopy=dataLenInBuf;			
		}
#endif
	// Our client can handle lots of data, more than we can
	else if(KBTCOMMCircularBufferLength<clientMaxLen)
		{		
		if(aContext->iClientRemainderToRead<=dataLenInBuf)
			{ // then this is the last chunk of a multi write to client
			if(aContext->iTerminatedReads)
				{ // try to find the terminated string
				// bytesToCopy here can be -1 
				// hence we have to check
				bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
				if(bytesToCopy==KErrNotFound)
					{// ternminator was requested but not found... what the heck
					bytesToCopy=aContext->iClientRemainderToRead;
					}
				}
			else
				{
				// this is the last chunk to be read for this client read
				// write as much as needed to the client
				bytesToCopy=aContext->iClientRemainderToRead;
				}
			}
		else if(aContext->ReadInBufferHighWatermarkReached()) // to minimise IPC
			{ 
			if(aContext->iTerminatedReads)
				{ // try to find the terminated string
				// bytesToCopy here can be -1 
				// hence we have to check
				bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
				if(bytesToCopy==KErrNotFound)
					{// terminator was not found... but
					// we have almost filled our buffer; write what we've got to the client
					bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,dataLenInBuf);
					__ASSERT_DEBUG(bytesToCopy==dataLenInBuf,PanicInState(EBTCommOpenStateWriteToClientPossibleLossOfData));
					
					// fill their buf as much as we can and continue
					readptr.SetLength(bytesToCopy);
					TInt offset=aContext->iClientWriteOffset;
					aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
					aContext->iClientRemainderToRead-=bytesToCopy;
					aContext->iClientWriteOffset+=bytesToCopy; // offset for next write to client
					return;	//our work is done wait for the next 
					}
				// else the terminator was found hence copy data up to the 
				// terminator and complete the read 
				}
			else // if no terminated reads were requested but the high watermark was reached
				{
				// copy what we have across and keep track of the offset
				bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,dataLenInBuf);
				__ASSERT_DEBUG(bytesToCopy==dataLenInBuf,PanicInState(EBTCommOpenStateWriteToClientPossibleLossOfData));
					
				// fill their buf as much as we can and continue
				readptr.SetLength(bytesToCopy);
				TInt offset=aContext->iClientWriteOffset;
				aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
				aContext->iClientRemainderToRead-=bytesToCopy;
				aContext->iClientWriteOffset+=bytesToCopy; // offset for next write to client
				return;	//our work is done wait for the next
				}
			} //matches else if(aContext->ReadInBufferHighWatermarkReached())
		// if the high watermark was not reached but we do terminated reads  
#ifndef BTCOMM_TEST_HIGH_WATERMARK 
		else if (aContext->iTerminatedReads) 
			{
			bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
			}
#endif
		}
	// complete if possible any terminated reads for smaller 
	// client buffers than ours
	else if(aContext->iTerminatedReads)
		{ // try to find the terminated string
		// bytesToCopy here cannot be greater than the clientMaxLen
		bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
		}	

	if(bytesToCopy>KErrNone) 
		{
		// do the copy to the client buffer
		TInt offset=aContext->iClientWriteOffset;
		readptr.SetLength(bytesToCopy);
		bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,bytesToCopy);
		aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
	
		// reset the offsets and counters and complete the read
		aContext->iClientReadLength=0;
		aContext->iClientReadPtr=0;
		aContext->iClientRemainderToRead=0;
		aContext->iClientWriteOffset=0;  
		
		aContext->iPort->ReadCompleted(KErrNone);
		}
	}

void TBTPortStateOpen::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
/**
	TBTPortState Open.
	Store the client side ptr and length and then signal
	start write immediately.
**/
	{
	LOG_FUNC
	// this is again due to the fact that we have to be asynchronous to avoid 
	// deadlocks with (mostly ESock) other comms components.
	if(aContext->IsWriteCancelPending())
		{
		aContext->iPort->WriteCompleted(KErrNotReady);
		//aContext->DoWriteCancel(); a deadlock example !
		return;
		}

	LOG(_L("**TBTPortStateOpen::Write**"));
	aContext->iClientWritePtr=aPtr;
	aContext->iClientWriteLength=aLength;
	aContext->iClientLengthWrittenSoFar=aLength;

	if (aLength>KBTCOMMSendBufferLength)
		{
		aContext->iClientLengthWrittenSoFar=KBTCOMMSendBufferLength;
		aContext->iMoreSendsToCome=ETrue;
		}
	TPtr8& sendptr=aContext->iSendBufPtr;
	sendptr.SetLength(0);
	// now read the client side data
	TPtr8 ptr((TUint8*)sendptr.Ptr(),0,aContext->iClientLengthWrittenSoFar);
	aContext->iPort->IPCRead(aPtr,ptr,0);

	sendptr.Append(ptr);
	aContext->iSendBufPtr.SetLength(aContext->iClientLengthWrittenSoFar);
	aContext->StartWriter();
	}

void TBTPortStateOpen::DoWriteCompleted(CBTPortProxy* aContext,TInt aError)
/**
	TBTPortStateOpen DoWriteCompleted.	
	If there is no error here, at this point the write 
	has successfully completed and
	can be signalled complete back to the client code.
**/
	{
	LOG_FUNC
	if (aContext->iMoreSendsToCome)
		{// we need to keep going 
		LOG(_L("**TBTPortStateOpen::DoWriteCompleted - more to come**"));

		TInt lentowrite=aContext->iClientWriteLength-aContext->iClientLengthWrittenSoFar;
		TInt lensofar=aContext->iClientLengthWrittenSoFar;
		if (lentowrite>KBTCOMMSendBufferLength)
			{// still more to come
			lentowrite=KBTCOMMSendBufferLength;
			aContext->iClientLengthWrittenSoFar+=KBTCOMMSendBufferLength;
			aContext->iMoreSendsToCome=ETrue;
			}
		else
			{
			aContext->iClientLengthWrittenSoFar+=lentowrite;
			aContext->iMoreSendsToCome=EFalse;
			}
		TPtr8 sendptr=aContext->iSendBufPtr;
		sendptr.SetLength(0);
		// read the client side data
		TPtr8 ptr((TUint8*)sendptr.Ptr(),0,lentowrite);
		aContext->iPort->IPCRead(aContext->iClientWritePtr,ptr,lensofar);
		
		sendptr.Append(ptr);
		aContext->iSendBufPtr.SetLength(lentowrite);
		aContext->StartWriter();
		}
	else
		{// we have finished - can signal to client and 0 the client stuff.
		LOG(_L("**TBTPortStateOpen::DoWriteCompleted - finished**"));
		aContext->iMoreSendsToCome=EFalse;
		aContext->iClientLengthWrittenSoFar=0;
		aContext->iClientWritePtr=0;                 
		aContext->iClientWriteLength=0;
		aContext->iPort->WriteCompleted(aError);
		}
	}

void TBTPortStateOpen::WriteCancel(CBTPortProxy* aContext)
/**
	It will complete the cancelation to the client and then request it from the server.
*/
	{
	LOG_FUNC
	// do say yes you cancelled and then cancel if necessary
	aContext->iPort->WriteCompleted(KErrCancel);
	aContext->SetWriteCancelPending();
	aContext->StartLocker();
	}

void TBTPortStateOpen::ReadCancel(CBTPortProxy* aContext)
/**
	It will complete the cancelation to the client.
*/
	{
	LOG_FUNC
#ifdef _DEBUG
	aContext->iReadsPending--;
#endif
	// do say yes you cancelled and then cancel if necessary
	aContext->iPort->ReadCompleted(KErrCancel);
	// Read Cancel does not need to be propagated to the underlying socket
	// since CSY has a ring buffer which is filled with inbound data regardless
	// of what the upper app is up to with its RComm
	}

void TBTPortStateOpen::DoLockedAction(CBTPortProxy* aContext)
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)	
		{
		aContext->StopLocker();
		Error(aContext,ret);
		return;
		}
	if(aContext->IsWriteCancelPending())
		{
		aContext->DoWriteCancel(); // the locked action for which we came in here
		}
	else //default handle - stray event must be caught
		{
		((TBTPortDefaultState*)this)->DoLockedAction(aContext);
		}
	aContext->StopLocker();
	//NOTE: we remain in this state
	}

void TBTPortStateOpen::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}

//		***** Closing state *****

TBTPortStateClosing::TBTPortStateClosing(CBTPortStateFactory* aParent)
	: TBTPortCommonBaseState(aParent)
	{
	LOG_FUNC
	}

TBTPortStateClosing::~TBTPortStateClosing()
	{
	LOG_FUNC
	}

void TBTPortStateClosing::Close(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortStateClosing::Read(CBTPortProxy* aContext, TAny* aPtr,TInt aLength)
/**
	Read in this state indirectly calls the DoLockedAction to get out and in the state machine again.
	This is done because of the huge window of opportunity for race conditions
	while the async timer is ticking before shutdown. Since we need the 
	shutdown timer for the closing to be async (to avoid deadlocks with Etel etc), 
	hence we should close the socket etc and transfer the Read() to the
	Idle state.
*/
	{
	LOG_FUNC
	
	LOG(_L("**TBTPortStateClosing Canceling the shutdown timer **"));
	aContext->CancelShutDownTimer();
	
	// cache the read
	aContext->iClientReadPtr=aPtr;
	aContext->iClientReadLength=aLength;
	aContext->iClientRemainderToRead=aLength;
	// close sockets resolver etc	
	aContext->StartLocker(); // next call is to our DoLockedAction(..);	
	// we now know that someone re-opened us while going down so...
	// transfer the call
	}

void TBTPortStateClosing::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
/**
	Write in this state indirectly calls the DoLockedAction to get out and in the state machine again.
	This is done because of the huge window of opportunity for race conditions
	while the async timer is ticking before shutdown. Since we need the 
	shutdown timer for the closing to be async (to avoid deadlocks with Etel etc), 
	hence we should close the socket etc and transfer the write() to the
	Idle state.
*/
	{
	LOG_FUNC

	LOG(_L("**TBTPortStateClosing Canceling the shutdown timer **"));
	aContext->CancelShutDownTimer();

	// cache the write
	aContext->iClientWritePtr=aPtr;
	aContext->iClientWriteLength=aLength;
	aContext->iClientLengthWrittenSoFar=aLength;
	
	// close sockets resolver etc
	//next call is in our DoLockedAction(..);
	aContext->StartLocker();
	// we now know that someone re-opened us while going down so...
	// transfer the call
	}

void TBTPortStateClosing::DoRunL(CBTPortProxy* aContext)
/**
	At this point our socket shutdown has returned.
	We can now close down the host resolver, netdb and finally
	the session to the socket server itself.  These are all 
	synchronous actions so must be handled by the locker.

	But we need to defer the actual completion of the final closing for later
	in order to give some time for other components like Etel to close first.
	Note the values used here are arbitrary and should be considered a 
	hacked fix :-(.

  	NOTE: the next transition is in TBTPortStateClosing::DoLockedAction 
	      unless the shutdown timer is cancelled by a new read or write 
**/
	{
	LOG_FUNC
	TInt ret=aContext->iStatus.Int();
	if (ret!=KErrNone)
		{// didn't manage to succeed with socket shutdown somehow.
		LOG(_L("**TBTPortStateClosing Closing with an error !**"));
		aContext->StartShutdownTimerL();// this will eventually call aContext->StartLocker()
		}
	else
		{// we have successfully shutdown the socket.
		aContext->StartShutdownTimerL();// this will eventually call aContext->StartLocker()		
		}
	//NOTE: the next transition is in TBTPortStateClosing::DoLockedAction 
	//      unless the shutdown timer is cancelled by a new read or write     
	}

void TBTPortStateClosing::DoLockedAction(CBTPortProxy* aContext)
/**
	TBTPortStateClosing DoLockedAction.
	At this point we have obtained the lock hence we are able to undertake
	the locked actions.
	This method should be called indirectly by the shutdown timer or due to
	a previous WriteCancel call at the open state, followed by a 
	Close() call ( which brought as here in this state).
**/
	{
	LOG_FUNC
	if(aContext->IsWriteCancelPending())
		{
		LOG(_L("**TBTPortStateClosing::DoLockedAction -- Servicing Write cancelations first**"));
		aContext->DoWriteCancel(); // the locked action for which we came in here
		aContext->StopLocker();
		return;
		}
		
	if(aContext->IsNetDbInUse())
		{
		// we need this in the case of cancelation before the netdb.Open()
		// because the kernel will panic us if it doesn't find the session
		RNetDatabase& netdb=aContext->iNetDatabase;
		netdb.Cancel();
		netdb.Close();
		aContext->SetNetDbNotInUse();
		LOG(_L("**TBTPortStateClosing, NetDB session clean-up done **"));
		}

	// the following immediate shutdown will not stiff C32 and is needed for 
	// the next close to be possible in ESock without it being blocked waiting
	// for the protocol SAP to be cleaned
	//
	// This shutdown will cause any outstanding socket ops to be cancelled so
	// we can safely call aContext->Cancel() afterwards.
	if(aContext->iSocket.SubSessionHandle())
		{// Make sure we had managed to open the socket connection before we arriveed here		
		aContext->iSocket.Shutdown(RSocket::EImmediate,aContext->iStatus);
		User::WaitForRequest(aContext->iStatus); 
		}
		
	// Do BT CSY specific locked action.
  	aContext->iSocket.Close();

	// We can only make synch calls to ESock from within the locker, otherwise 
	// we risk deadlock.  This means we can't put any Cancel()s that may end
	// up in ESock in the port proxy's DoCancel() unless we guarantee that Cancel()
	// can only be called from within the locker.  Instead we ensure we've done
	// all the necessary cancelling before calling Cancel().
	aContext->Cancel();
   

	// We should never transition to the closing state whilst waiting for
	// an ESock connection, unless I've bugged the state machine.
	__ASSERT_DEBUG(!aContext->iSockServConnector->IsActive(), PanicInState(EBTCommCloseWhileWaitingForSockServHandle));

   	if(aContext->iSockServ.Handle())
   		{
  		aContext->iSockServ.Close();
   		}
   	aContext->StopLocker();	// sync actions completed ok - now stop locker.

	// we do this just before destruction, just in case someone has reopened us
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EIdle));
	// UGLY
	aContext->DestructContext(); // will not do anything if we got reopened
	}

void TBTPortStateClosing::DoWriteCompleted(CBTPortProxy * /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}

void TBTPortStateClosing::DoReadCompleted(CBTPortProxy * /*aContext*/, TInt /*aError*/)
	{
	LOG_FUNC
	}

void TBTPortStateClosing::LogStateError(CBTPortProxy * /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}


// Error State

TBTPortErrorState::TBTPortErrorState(CBTPortStateFactory* aParent) 
	: TBTPortState(aParent)
	{
	LOG_FUNC
	}

TBTPortErrorState::~TBTPortErrorState()
	{
	LOG_FUNC
	}

void TBTPortErrorState::Open(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::Read(CBTPortProxy* aContext,TAny* /*aPtr*/,TInt /*aLength*/)
/**
	Notify the client about this recent error.
*/	
	{
	LOG_FUNC
	aContext->iPort->ReadCompleted(aContext->iLastError);
	}

void TBTPortErrorState::Write(CBTPortProxy* aContext,TAny* /*aPtr*/,TInt /*aLength*/)
/**
	Notify the client about this recent error.
*/	
	{
	LOG_FUNC
	aContext->iPort->WriteCompleted(aContext->iLastError);
	}

void TBTPortErrorState::Close(CBTPortProxy* aContext)
	{
	LOG_FUNC
	aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
		
	// check to see if the error came before we managed to do the locked action 
	// ..and cancel the pending locked action then
	if(aContext->IsLockerOn()) 
		{            
		aContext->StopLocker();
		}
	aContext->SetActive();
	TRequestStatus* pStatus = &(aContext->iStatus);
	User::RequestComplete(pStatus, KErrNone); 
	}

void TBTPortErrorState::DoRunL(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::DoCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::WriteCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::ReadCancel(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::DoLockedAction(CBTPortProxy* /*aContext*/)
	{
	LOG_FUNC
	BAD_BTCOMM_EVENT
	}

void TBTPortErrorState::DoWriteCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::DoReadCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
	{
	LOG_FUNC
	}

void TBTPortErrorState::Error(CBTPortProxy* aContext,TInt aError)
/**
	Error will be the first method to be called in entering this state.
	So here Error will check to see if there are any Queued reads, which 
	it will immediately complete.
	
	This should only be called on moving from any other state to the error 
	state and not otherwise.
 */
	{
	LOG_FUNC
	if (aContext->iClientReadPtr)
		{ // this means that a read was queued already so we need to signal 
		  // the error immediately
		aContext->iPort->ReadCompleted(aError);		
		}
	if (aContext->iClientWritePtr)
		{ // same for write
		aContext->iPort->WriteCompleted(aError);
		}
	}

