bluetooth/btstack/avdtp/avdtpSignallingChannel.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 15 Jan 2010 08:13:17 +0200
changeset 0 29b1cd4cb562
permissions -rw-r--r--
Revision: 200951_001

// Copyright (c) 2003-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:
// Implements the avdtp signalling channel
// 
//

/**
 @file
 @internalComponent
*/

#include <bluetooth/logger.h>
#include "avdtpSignallingChannel.h"
#include "avdtp.h"
#include "avdtputil.h"
#include "avdtpSignallingTransaction.h"

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

//Macro to just return (and enter debugger in debug builds) if we get an unsolicited response packet
#define RETURN_IF_NO_TRANSACTION(transaction) 	if (!transaction) {	LOG(_L("No transaction found"));return;}
#define RETURN_IF_SIGNAL_BAD(transaction, signal) if (!CheckSignal(*transaction, signal)) {LOG1(_L("Transaction has different signal (%d)"), signal); return;}

// check that sessions have replied with an AVDTP error in error cases
#define	CHECK_ERROR(result) if (!(result<=KErrAvdtpSignallingErrorBase || result==KErrNone || result==KErrNotFound)) __DEBUGGER();

CSignallingChannel* CSignallingChannel::NewL(CAvdtpProtocol& aProtocol,
											 CLogicalChannelFactory& aChannelFactory,
											 const TBTDevAddr& aRemoteAddress)
	{
	LOG_STATIC_FUNC
	CSignallingChannel* s = CSignallingChannel::NewLC(aProtocol, aChannelFactory, aRemoteAddress);
	CleanupStack::Pop();
	return s;
	}

CSignallingChannel* CSignallingChannel::NewLC(CAvdtpProtocol& aProtocol,
											  CLogicalChannelFactory& aChannelFactory,
											  const TBTDevAddr& aRemoteAddress)
	{
	LOG_STATIC_FUNC
	CSignallingChannel* s = new(ELeave)	CSignallingChannel(aProtocol, aChannelFactory, aRemoteAddress);
	CleanupStack::PushL(s);
	s->ConstructL();
	return s;
	}
	
CSignallingChannel::CSignallingChannel(CAvdtpProtocol& aProtocol,
									   CLogicalChannelFactory& aChannelFactory,
									   const TBTDevAddr& aRemoteAddress)
: iProtocol(aProtocol),
  iLogicalChannelFactory(aChannelFactory),
  iPermanentUsers(_FOFF(XAvdtpSignalReceiver,iSignalReceiverEmbeddedLink)),
  iTransactions(_FOFF(CSignallingTransaction,iLink)),
  iRemoteAddress(aRemoteAddress),
  iInboundMessage(*this),
  iDraftMessages(_FOFF(CAvdtpOutboundSignallingMessage,iLink)),
  iQueuedMessages(_FOFF(CAvdtpOutboundSignallingMessage,iLink))
	{
	LOG_FUNC
	SetBlocked(EFalse);
	
	TCallBack cb(TryToClose, this);
	iIdleTimerEntry.Set(cb);
	}
	
void CSignallingChannel::ConstructL()
	{
	LOG_FUNC
	TCallBack cb(CSignallingChannel::TryToSendCallback, this);
	iTryToSendCallback = new (ELeave)CAsyncCallBack(cb, CActive::EPriorityStandard);
	}

CSignallingChannel::~CSignallingChannel()
	{
	LOG_FUNC
	CancelIdleTimer();

	// cancel possible outstanding factory request
	iLogicalChannelFactory.Cancel(iLogicalChannelRequest);
	
	// remove from protocol's knowledge
	iProtocol.SignallingChannelDown(*this);
	
	// any preauthorisation for device this represents is now void
	if (!IsListening())
		{
		iProtocol.RemoteSEPCache().InvalidateSEPs(iRemoteAddress);
		(void)iProtocol.SetPreauthorisation(iRemoteAddress, EFalse);
		}
		
	if (iBearer)
		{
		// tell l2cap
		iBearer->Shutdown(CServProviderBase::EImmediate);
		}
		
	delete iBearer;
	delete iTryToSendCallback;
	}

void CSignallingChannel::StartTryToSendCallback()
	{
	LOG_FUNC
	if (!iTryToSendCallback->IsActive())
		{
		iTryToSendCallback->CallBack();
		}
	// else already running and will callback anyway
	}

void CSignallingChannel::CancelTryToSendCallback()
	{
	LOG_FUNC
	iTryToSendCallback->Cancel();
	}

/*static*/ TInt CSignallingChannel::TryToSendCallback(TAny *aSigCh)
	{
	LOG_STATIC_FUNC
	return (reinterpret_cast<CSignallingChannel*>(aSigCh))->TryToSendCallback();
	}

void CSignallingChannel::ObtainMTU()
	{
	LOG_FUNC
	TPckg<TInt> mtubuf(iBearerMTU);
	TInt err=iBearer->GetOption(KSolBtL2CAP,KL2CAPOutboundMTUForBestPerformance,mtubuf);
	if (err!=KErrNone)
		{
		Error(err);
		}
	}

/*
Does post-packet-send processing
*/
void CSignallingChannel::PacketSent(CSignallingTransaction& aTransaction)
	{
	LOG_FUNC
	// We can't delete the transaction yet, because it looks after timer.
		
	switch (aTransaction.SentAction())
		{
		case EKeepSetRTX:
			{
			aTransaction.StartTimer();
			break;
			}
		case EDiscard:
			{
			// these transactions can be cleared up now
			// we don't support caching of responses if they get lost - they'll be regenerated if needed
			aTransaction.iLink.Deque();
			delete &aTransaction;
			break;
			}
		case EKeepDontSetRTX:
		// do nothing!
			break;
		default:
			__ASSERT_DEBUG(0, Panic(EAvdtpUnknownPostSendAction));
		}
	}
	
void CSignallingChannel::CheckOutboundQueue()
	{
	// check to see if the outbound queue is empty but we have an outstanding
	// request to service it
	if (iQueuedMessages.IsEmpty() && iTryToSendCallback->IsActive())
		{
		CancelTryToSendCallback();
		}				
	}
	
void CSignallingChannel::ServiceOutboundQueue()
	{
	LOG_FUNC
	if (!Blocked())
		{
		// we can send!
		// get fragments to send if we don't have any from before
		if(iOutgoingSignallingMessage.IsEmpty())
			{
			// get the head message
			CAvdtpOutboundSignallingMessage& message = *iQueuedMessages.First();
			
			// find the transaction pertaining to it (and make it the current transaction)
			iCurrentTransaction = FindTransaction(message.TransactionLabel());
			
			__ASSERT_DEBUG(iCurrentTransaction!=NULL, Panic(EAvdtpSignallingChannelDrainingFaulty));
			
			// see whether this requires fragmenting, and get next fragment
			// note reference return parameter
			TRAPD(ret, iMoreFrags = message.GetNextOutboundFragmentL(iOutgoingSignallingMessage, iBearerMTU));
			if (ret!=KErrNone)
				{
				iMoreFrags = EFalse;
				iOutgoingSignallingMessage.Free();
				// error the user
				iCurrentTransaction->Error(ret);
				return;
				}
			}
			

		// write outgoing fragments to bearer
		TInt sent = iBearer->Write(iOutgoingSignallingMessage,NULL);
		
		if (!sent)
			{
			// leave fragment there for next time writing
			SetBlocked(ETrue);
			}
		else
			{
			iOutgoingSignallingMessage.Free();
			if (!iMoreFrags)
				{
				// that message has been completely sent
				PacketSent(*iCurrentTransaction);
				}
			}

		if (!iQueuedMessages.IsEmpty())
			{
			// more to send, send asynchronously.
			StartTryToSendCallback();
			}
		else if (iTryToSendCallback->Priority()==KAvdtpReleaseAcceptPriority)
            {
            // cancel the Release send boost (actually might want to use boosting
            // to clear remaining fragments of partially sent commands)
            iTryToSendCallback->SetPriority(CActive::EPriorityStandard);
            }
   		}
	}

/*static*/ TInt CSignallingChannel::TryToSendCallback()
	{
	LOG_STATIC_FUNC
	ServiceOutboundQueue();
	return EFalse;
	}

TBTDevAddr CSignallingChannel::RemoteAddress()
	{
	LOG_FUNC
	if (iBearer && IsListening())
		{
		TL2CAPSockAddr remote;
		iBearer->RemName(remote);		
		iRemoteAddress = remote.BTAddr();
		}
	if (iBearer)
		{
		return iRemoteAddress;
		}
	else
		{
		return TBTDevAddr(0);
		}
	}

void CSignallingChannel::LogicalChannelFactoryRequestComplete(TLogicalChannelFactoryTicket aTicket, TInt aError)
	{
	LOG_FUNC
	
	if (aError == KErrNone)
		{
		iLogicalChannelRequest = aTicket;

		TLogicalChannelRecord rec = aTicket.GetLogicalChannel();
		
		// we take this "out" of the logical channel for performance reasons
		// (the pending logical channel will then die at some point later)
		iBearer = rec.iLogicalChannelSAP;
		iBearer->SetNotify(this);
		
		// get the MTU now
		ObtainMTU();

		// decide on our address (we may have been passive)
		// to let clients know we signal all the Signal Receivers
		TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	
		while (iter)
			{
			iter++->SignallingChannelReady(*this);
			}
			
		// migrate all the current listening sessions, to this new channel
		// if their clients not happy they can detach and relisten for new connection
		// just as if they got an inbound connection - which this is like
		// whether it really is passive l2cap, or was initiated by any one session
		
		iProtocol.ConnectSignallingListeners(*this);
		
		// process any already received inbound data
		if (rec.iDataCount)
			{
			NewData(rec.iDataCount);
			}
		if (rec.iEndOfData)
			{
			NewData(KNewDataEndofData);
			Disconnect();
			}
		}
	else
		{
		// to let clients know we signal all the Signal Receivers
		TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	
		while (iter)
			{
			iter++->SignallingChannelError(aError);
			}
		if (IsIdle())
			{
			IdledD();
			}
		}
	}
	
TInt CSignallingChannel::AttachSignallingUser(XAvdtpSignalReceiver& aUser)
	{
	LOG_FUNC
	// must check they're not already in the queue
#ifdef _DEBUG
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	while (iter)
		{
		if (iter++ == &aUser)
			{
			Panic(EAvdtpBadSignallingUserQueue);
			}
		}
#endif

	TBool isFirstUser = iPermanentUsers.IsEmpty();
	TInt ret = KErrNone;
	
	iPermanentUsers.AddFirst(aUser);
	if (iBearer)
		{
		aUser.SignallingChannelReady(*this);
		}
	else if(isFirstUser)
		{
		// kick off the need for a signalling channel - we have a user
		
		// form a request
		if (!IsListening())
			{
			TRAP(ret, iLogicalChannelRequest = iLogicalChannelFactory.CreateSignallingLogicalChannelL(iRemoteAddress, *this));
			}
		else
			{
			TRAP(ret, iLogicalChannelRequest = iLogicalChannelFactory.ExpectSignallingLogicalChannelL(*this));
			}
		}
	
	return ret;
	}

void CSignallingChannel::DetachSignallingUser(XAvdtpSignalReceiver& aUser)
	{
	LOG_FUNC
	aUser.iSignalReceiverEmbeddedLink.Deque();
	
	// cleanup any requests they had outstanding
	RemoveTransactions(aUser);

	if (IsIdle())
		{
		if(!IsListening())
			{
			QueIdleTimer();
			}
		else
			{
			//listener just dies
			IdledD();
			}
		}
	}

TBool CSignallingChannel::IsIdle() const
	{
	LOG_FUNC

	// idle if permanent users empty, and transient users haven't got stuff queued
	return (iPermanentUsers.IsEmpty() && iTransactions.IsEmpty());
	}
	
void CSignallingChannel::QueIdleTimer()
	{
	LOG_FUNC
	CancelIdleTimer();
	iIdleTimerActive = ETrue;	
	BTSocketTimer::Queue(KAvdtpSignallingChannelIdleTimeout, iIdleTimerEntry);
	}
	
void CSignallingChannel::CancelIdleTimer()
	{
	LOG_FUNC
	if(iIdleTimerActive)
		{
		// there's something to cancel
		iIdleTimerActive = EFalse;
		BTSocketTimer::Remove(iIdleTimerEntry);
		}
	}

/*static*/ TInt CSignallingChannel::TryToClose(TAny* aSignallingChannel)
	{
	LOG_STATIC_FUNC
	// check if still idle
	CSignallingChannel* sigch = reinterpret_cast<CSignallingChannel*>(aSignallingChannel);
	if (sigch->IsIdle())
		{
		sigch->IdledD();
		}
	return EFalse;
	}

void CSignallingChannel::IdledD()
	{
	LOG_FUNC
	// we are till idle
	__ASSERT_ALWAYS(iDraftMessages.IsEmpty(), Panic(EAvdtpSignallingChannelDyingWithDraftMessagesOnQueue));
	__ASSERT_ALWAYS(iTransactions.IsEmpty(), Panic(EAvdtpSignallingChannelDyingWithTransactionsOnQueue));
	__ASSERT_ALWAYS(iQueuedMessages.IsEmpty(), Panic(EAvdtpSignallingChannelDyingWithMessagesOnQueue));

	delete this;
	}
	
/*
Remove transactions held by a user
*/
void CSignallingChannel::RemoveTransactions(XAvdtpSignalReceiver& aUser)
	{
	LOG_FUNC
	TDblQueIter<CSignallingTransaction> iter(iTransactions);
	while (iter)
		{
		CSignallingTransaction* transaction = iter++;
		if (transaction->User() == &aUser)
			{
			transaction->iLink.Deque();
			delete transaction;
			
			// outbound queue may now be empty
			CheckOutboundQueue();			
			}
		}
	}

void CSignallingChannel::NewData(TUint aCount)
	{
	LOG_FUNC
	// read out now (could make this async though)
	// just give to inbound message
	TInt res = iInboundMessage.NewData(aCount);
	
	if (res!=KErrNone)
		{
		// have to error this channel
		Error(res);
		}
	}
	

/**
Called by packet as it builds itself up - get stuff out of bearer now
The service provided is always to fetch an MTU's worth
*/
TInt CSignallingChannel::GetData(RMBufChain& aData, TUint aOptions, TSockAddr* aAddr)
	{
	LOG_FUNC
	return iBearer->GetData(aData,iBearerMTU,aOptions,aAddr);
	}
	

/**
Upcall from L2CAP - it is now ready to send
*/	
void CSignallingChannel::CanSend()
	{
	LOG_FUNC
	SetBlocked(EFalse);
	StartTryToSendCallback();
	}
	
void CSignallingChannel::ConnectComplete()
	{
	LOG_FUNC
	// the factory would have done this by now
	}
	
void CSignallingChannel::ConnectComplete(const TDesC8& /*aConnectData*/)
	{
	LOG_FUNC
	// the factory would have done this by now
	}
	
void CSignallingChannel::ConnectComplete(CServProviderBase& /*aSSP*/)
	{
	LOG_FUNC
	// the factory would have done this by now
	}
	
void CSignallingChannel::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/)
	{
	LOG_FUNC
	// the factory would have done this by now
	}
	
void CSignallingChannel::CanClose(TDelete /*aDelete*/)
	{
	LOG_FUNC
	// only support immediate shutdown so shouldnt be called
	}
	
void CSignallingChannel::CanClose(const TDesC8& /*aDisconnectData*/,TDelete aDelete)
	{
	LOG_FUNC
	// l2cap shouldnt upcall this, but just in case
	CanClose(aDelete);
	}
	
void CSignallingChannel::Error(TInt aError,TUint /*aOperationMask*/)
	{
	LOG_FUNC
	// from logical channel
	ErrorPermanentUsers(aError);
	ErrorServiceRequesters(aError);
	delete this;
	}
	
void CSignallingChannel::ErrorPermanentUsers(TInt aError)
	{
	LOG_FUNC
	// error all users
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	while (iter)
		{
		iter++->SignallingChannelError(aError);
		}
	}

void CSignallingChannel::ErrorServiceRequesters(TInt aError)
	{
	LOG_FUNC
	// complete all outstanding services
	TDblQueIter<CSignallingTransaction> iter(iTransactions);
	while (iter)
		{
		CSignallingTransaction* transaction = iter++;

		if (transaction->User())
			{
			// it was a command, tell the user it failed
			// This will cause the transaction to be tidied
			// up.
			transaction->Error(aError);
			}
		else
			{
			// Just tidy up, there's no user to inform.
			RemoveTransaction(*transaction);
			}					
		}
	}
	
void CSignallingChannel::CancelTransactions(XAvdtpSignalReceiver& aUser)
	{
	LOG_FUNC
	RemoveTransactions(aUser);
	}
	
void CSignallingChannel::Disconnect()
	{
	LOG_FUNC
	Error(KErrDisconnected);
	}
	
void CSignallingChannel::Disconnect(TDesC8& /*aDisconnectData*/)
	{
	LOG_FUNC
	// shouldnt be supported by l2cap, but just in case
	Disconnect();
	}
	
void CSignallingChannel::IoctlComplete(TDesC8* /*aBuf*/)
	{
	LOG_FUNC
	// we don't issue ioctls - drop
	}
	
void CSignallingChannel::NoBearer(const TDesC8& /*aConnectionInfo*/)
	{
	LOG_FUNC
	// do nothing
	}
	
void CSignallingChannel::Bearer(const TDesC8& /*aConnectionInfo*/)
	{
	LOG_FUNC
	// do nothing	
	}
		
/**
Find a transaction given a transaction label - there shuold be one
@param aLabel the transaction label in the packet for which a transaction is required
@internalComponent
*/
CSignallingTransaction* CSignallingChannel::FindTransaction(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	TDblQueIter<CSignallingTransaction> iter(iTransactions);
	while (iter)
		{
		CSignallingTransaction* t = iter;
		if (t->Label() == aLabel)
			{
			return t;
			}
		iter++;
		}
	//__DEBUGGER();// FLOG
	return NULL;
	}

/**
Relinquish a transaction - may not be found if race with client cancelling
@param aLabel the label of the transaction to relinquish
@internalComponent
**/	
void CSignallingChannel::RemoveTransaction(CSignallingTransaction& aTransaction)
	{
	LOG_FUNC

	// This will cancel the timer and remove the transaction from the queue
	delete &aTransaction;

	// outbound queue may now be empty
	CheckOutboundQueue();			
	
	if (IsIdle())
		{
		QueIdleTimer();
		}
	}
	
void CSignallingChannel::DiscoverIndication(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	// create transaction ready for the response
	CSignallingTransaction* transaction = PrepareSignallingResponse(EResponseAccept, EAvdtpDiscover, aLabel);
	
	if (transaction)
		{
		// go and ask sessions to contribute to packet
		TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
		
		while (iter)
			{
			// sessions MUST process this synchronously.
			(void)(iter++)->DiscoverIndication(aLabel, transaction->Message());
			}
		
		// ..as we now deem the packet constructed, and ready to send
		(void)EnqueueMessage(*transaction);
		// if Enque failed (it can't at present for Responses) remote would timeout
		}
	}
	
	

void CSignallingChannel::DiscoverConfirm(TAvdtpTransactionLabel aLabel,
										  TInt aResult,
										  const TAvdtpInternalDiscoverConfirm* const aConfirm)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);	
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpDiscover);
		
	transaction->User()->DiscoverConfirm(aResult, aConfirm);
	
	RemoveTransaction(*transaction); //clears RTX timer
	}

void CSignallingChannel::GetCapsIndication(TAvdtpTransactionLabel aLabel, TSEID aSEID)
	{
	LOG_FUNC
	// create transaction ready for the response
	CSignallingTransaction* transaction = PrepareSignallingResponse(EResponseAccept, EAvdtpGetCapabilities, aLabel);
	if (transaction)
		{
		// go and ask sessions to contribute to packet
		TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
		TInt result = KErrNotFound;

		while (iter)
			{
			// sessions MUST process this synchronously.
			result = (iter++)->GetCapsIndication(aLabel, aSEID, transaction->Message());
			if (result!=KErrNotFound)
				{
				// some error occured that meant we need not bother asking other sessions
				break;
				}
			}
	
		// ..as we now deem the packet constructed, and ready to send
		if (result==KErrNone)
			{
			(void)EnqueueMessage(*transaction);
			// if Enque failed (it can't at present for Responses) remote would timeout
			}
		else
			{
			delete transaction;
			
			if (result==KErrNotFound)
				{
				SendReject(aLabel, EAvdtpGetCapabilities, EAvdtpBadACPSEID);
				}
			// else dump, and leave to time out				
			}
		}
	else
		{
		// leave to timeout - no apt error
		}
	}

void CSignallingChannel::GetCapsConfirm(TAvdtpTransactionLabel aLabel, TInt aResult, const HBufC8* aCaps/*=NULL*/)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpGetCapabilities);
	
	TSEID seid(reinterpret_cast<TUint>(transaction->Cookie()));
	if (aResult==KErrNone)
		{
		//Parse the SEID info, updating the protocol's sep cache and
		//generating a descriptor to pass to the client.
		
		// tricky to get this to be efficient
		// we just tell cache - it'll parse if it chooses to :o)
		iProtocol.RemoteSEPCache().SetCapabilities(iRemoteAddress,
												   seid,
												   const_cast<HBufC8*>(aCaps));
														   	
			
		// to make life for the user we tell the the categories we did see
		// if they do want to investigate more they can begin a deep parse
		TAvdtpServiceCategories  cats;
		TRAPD(err, cats = iProtocol.RemoteSEPCache().GetCapabilitiesL(
											iRemoteAddress,
											seid));
											
		transaction->User()->GetCapsConfirm(err, seid,cats());
		}
	else
		{
		transaction->User()->GetCapsConfirm(aResult,seid, NULL);
		}
	RemoveTransaction(*transaction); //clears RTX timer
	}


void CSignallingChannel::SetConfigIndication(TAvdtpTransactionLabel aLabel,
											  TSEID aACPSEID,
											  TSEID aINTSEID,
											  RBuf8& aConfigData)
	{		
	LOG_FUNC
	// ownership of aConfigData transferred
	// a consuming session will take owership
	// if it is unclaimed we tidyup
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->SetConfigIndication(aLabel, aACPSEID, aINTSEID, aConfigData);
		if (result!=KErrNotFound)
			{
			break;
			}
		}
			
	if (result!=KErrNone && result!=KErrNoMemory)
		{
		CHECK_ERROR(result);
		TAvdtpSignallingErrorCode code = AvdtpInternalUtils::SymbianErrorToAvdtpError(result);
		
		// as per contract, the session has given extended info back to us
		SendSetConfigurationReject(aLabel, code, static_cast<TAvdtpServiceCategory>(aConfigData[0]));
		
		aConfigData.Close();
		}
	else if (result == KErrNoMemory)
		{
		// if no memory then leave for remote to timeout
		aConfigData.Close();
		}
	}
		

void CSignallingChannel::SetConfigConfirm(TAvdtpTransactionLabel aLabel, TInt aResult, TAvdtpServiceCategory aErrorCategory)
	{
	LOG_FUNC
	ConfigConfirm(aLabel, aResult, aErrorCategory, EFalse);
	}


void CSignallingChannel::GetConfigIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID)
	{	
	LOG_FUNC
	// create transaction ready for the response
	CSignallingTransaction* transaction = PrepareSignallingResponse(EResponseAccept, EAvdtpGetConfiguration, aLabel);
	
	if(transaction)
		{
		// go and ask sessions to contribute to packet
		TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
		TInt result = KErrNotFound;

		while (iter)
			{
			// sessions MUST process this synchronously.
			result = (iter++)->GetConfigIndication(aLabel, aACPSEID, transaction->Message());
			if (result!=KErrNotFound)
				{
				break;
				}
			}
		
		// ..as we now deem the packet constructed, and ready to send
		if (result==KErrNone)
			{
			(void)EnqueueMessage(*transaction);
			// leave remote to timeout
			}
		else if (result!=KErrNoMemory)
			{
			delete transaction;
			SendReject(aLabel, EAvdtpGetConfiguration, AvdtpInternalUtils::SymbianErrorToAvdtpError(result));
			}
		// else leave to timeout, no suitable response to send
		}
	// else leave to timeout, no suitable response to send
	}


void CSignallingChannel::GetConfigConfirm(TAvdtpTransactionLabel /*aLabel*/, TInt /*aResult*/)
	{
	LOG_FUNC
	// shouldnt get one of these as we wont send GetConf yet
	// drop
	}


void CSignallingChannel::ReconfigIndication(TAvdtpTransactionLabel aLabel,
										TSEID aACPSEID,
										 RBuf8& aConfigData)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
		
	while (iter)
		{
		result = (iter++)->ReconfigIndication(aLabel, aACPSEID, aConfigData);
		if (result==KErrNone)
			{
			break;
			}
		}
	
	if (result!=KErrNone && result!=KErrNoMemory)
		{
		CHECK_ERROR(result);
	
		TAvdtpSignallingErrorCode code = AvdtpInternalUtils::SymbianErrorToAvdtpError(result);
		// as per contract, the session has given extended info back to us
		SendReconfigureReject(aLabel, code, static_cast<TAvdtpServiceCategory>(aConfigData[0]));
		
		aConfigData.Close();
		}
	else if (result != KErrNone)
		{
		// clean up the config data if we failed - it hasn't been passed on
		aConfigData.Close();
		}
	// else leave remote to timeout
	}

void CSignallingChannel::ReconfigConfirm(TAvdtpTransactionLabel aLabel, TInt aResult, TAvdtpServiceCategory aErrorCategory)
	{
	LOG_FUNC
	ConfigConfirm(aLabel, aResult, aErrorCategory, ETrue);
	}

TInt CSignallingChannel::SendOpenStream(XAvdtpSignalReceiver& aReceiver, TSEID aACPSEID)
	{
	LOG_FUNC
	if (!iBearer)
		{
		return KErrNotReady;
		}

	TInt result = KErrNoMemory;
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpOpen);
	
	if (transaction)
		{
		result = AvdtpSignallingMessageOpen::Command::Format(transaction->Message(), aACPSEID);
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
	return result;
	}
	
void CSignallingChannel::OpenIndication(TAvdtpTransactionLabel aLabel, TSEID aSEID)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
		
	while (iter)
		{
		result = (iter++)->OpenIndication(aSEID); 
		if (result!=KErrNotFound)
			{
			break;
			}
		}	
	
	if (result==KErrNone)
		{
		result = SendAccept(aLabel, EAvdtpOpen);
		}
	else if (result!=KErrNone && result!=KErrNoMemory)
		{
		SendReject(aLabel, EAvdtpOpen, AvdtpInternalUtils::SymbianErrorToAvdtpError(result));
		}

	if (result==KErrNone)
		{
		// now we are happy to pre-authorise remote for more incoming logical channels
		iProtocol.SetPreauthorisation(iRemoteAddress, ETrue);
		}
//#pragma message("would be nice to error session that consumed")
	}


void CSignallingChannel::OpenConfirm(TAvdtpTransactionLabel aLabel, TInt aResult)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpOpen);
			
	transaction->User()->OpenConfirm(aResult, TSEID(reinterpret_cast<TUint>(transaction->Cookie())));
	RemoveTransaction(*transaction); //clears RTX timer
	}


void CSignallingChannel::StartIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->StartIndication(aLabel, aACPSEID);
		if (result!=KErrNotFound)
			{
			break;
			}
		}

	if (result != KErrNone && result!=KErrNoMemory)
		{
		CHECK_ERROR(result);		
		
		// this one has to error slightly differently - we tell remote of the first bad SEID
		// which would be this one.
		TPckg<TUint8> seidPckg(aACPSEID.PacketValue());
		SendReject(aLabel, EAvdtpStart, AvdtpInternalUtils::SymbianErrorToAvdtpError(result), &seidPckg);
		}
	//else leave remote to timeout
	}

void CSignallingChannel::StartConfirm(TAvdtpTransactionLabel aLabel, TInt aResult)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpStart);
	
	transaction->User()->StartConfirm(aResult, TSEID(reinterpret_cast<TUint>(transaction->Cookie())));
	RemoveTransaction(*transaction); //clears RTX timer
	}

void CSignallingChannel::SuspendConfirm(TAvdtpTransactionLabel aLabel, TInt aResult)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpSuspend);
		
	transaction->User()->SuspendConfirm(aResult, TSEID(reinterpret_cast<TUint>(transaction->Cookie())));
	RemoveTransaction(*transaction); //clears RTX timer
	}


void CSignallingChannel::ReleaseIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->ReleaseIndication(aLabel, aACPSEID);
		if (result!=KErrNotFound)
			{
			break;
			}
		}

	// session must send accept otherwise we may send OK after it has released transport channels	
	if (result!=KErrNone && result!=KErrNoMemory)
		{
		result = SendReject(aLabel, EAvdtpRelease, AvdtpInternalUtils::SymbianErrorToAvdtpError(result));
		}
	// else leave remote to timeout
	if (result!=KErrNone)
		{
//	#pragma message("would be nice to just error the session that consumed")
//			iSignallingChannelError(err);
		}
	}

void CSignallingChannel::ReleaseConfirm(TAvdtpTransactionLabel aLabel, TInt aResult)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpRelease);
	
	// We take the details we need and remove transaction before
	// passing the signal on for processing as ReleaseConfirm may
	// lead to the stream, and all its transactions, being 
	// destroyed.
	XAvdtpSignalReceiver* user = transaction->User();
	TSEID seid(reinterpret_cast<TUint>(transaction->Cookie()));
	RemoveTransaction(*transaction); //clears RTX timer

	if (user)
		{
		// it was an "normal" Release - forward confirm
		user->ReleaseConfirm(aResult, seid);
		}
	}


void CSignallingChannel::SuspendIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->SuspendIndication(aLabel, aACPSEID);
		/* if we've found and suspended we don't need to go round again
		   if we've run out of memory or anything then...
		*/
		if (result!=KErrNotFound)
			{
			break;
			}
		}

	/* if we've not broken out then we will come out with notfound
	   which is a bad sepid */
	if (result!=KErrNone && result!=KErrNoMemory)
		{
		CHECK_ERROR(result);		
		
		// this one has to error slightly differently - we tell remote of the first bad SEID
		// which would be this one.
		TPckg<TUint8> seidPckg(aACPSEID.PacketValue());
		SendReject(aLabel, EAvdtpSuspend, AvdtpInternalUtils::SymbianErrorToAvdtpError(result), &seidPckg);
		}
	// else leave remote to timeout
	}


void CSignallingChannel::AbortIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->AbortIndication(aLabel, aACPSEID);
		if (result!=KErrNotFound)
			{
			break;
			}
		}
		
	if (result == KErrNone)
		{
		(void)SendAccept(aLabel, EAvdtpAbort);
		}
	// else no error path for Abort. Just ignore.			
	}

void CSignallingChannel::AbortConfirm(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpAbort);

	// We take the details we need and remove transaction before
	// passing the signal on for processing as AbortConfirm may
	// lead to the stream, and all its transactions, being 
	// destroyed.
	XAvdtpSignalReceiver* user = transaction->User();
	TSEID seid(reinterpret_cast<TUint>(transaction->Cookie()));
	RemoveTransaction(*transaction); //clears RTX timer

	if (user)
		{
		// it was an "immediate" Abort - noone wants confirm
		user->AbortConfirm(seid);
		}
	}


void CSignallingChannel::SecurityControlIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID, const HBufC8* aSecurityData)
	{
	LOG_FUNC
	TDblQueIter<XAvdtpSignalReceiver> iter(iPermanentUsers);
	TInt result = KErrNotFound;
	
	while (iter)
		{
		result = (iter++)->SecurityControlIndication(aLabel, aACPSEID, aSecurityData);
		if (result!=KErrNotFound)
			{
			break;
			}
		}

	if (result!=KErrNone && result!=KErrNoMemory)
		{
		CHECK_ERROR(result);		
		
		// destroy HBuf
		delete (const_cast<HBufC8*>(aSecurityData));
		SendReject(aLabel, EAvdtpSecurityControl, AvdtpInternalUtils::SymbianErrorToAvdtpError(result));
		}
	// else ownership of HBufC8 transferred
	}

void CSignallingChannel::SecurityControlConfirm(TAvdtpTransactionLabel aLabel, TInt aResult, const TDesC8& aResponseData)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);	
	RETURN_IF_SIGNAL_BAD(transaction, EAvdtpSecurityControl);

	transaction->User()->SecurityControlConfirm(aResult, TSEID(reinterpret_cast<TUint>(transaction->Cookie())), aResponseData);
	RemoveTransaction(*transaction); //clears RTX timer
	}


/**
Send primitives
**/


TInt CSignallingChannel::SendDiscoverSEPs(XAvdtpSignalReceiver& aReceiver)
	{
	LOG_FUNC
	if (!iBearer)
		{
		return KErrNotReady;
		}
		
	CSignallingTransaction*	transaction = PrepareSignallingCommand(aReceiver, EAvdtpDiscover);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result=EnqueueMessage(*transaction);
		}
	if (result!=KErrNone)
		{
		delete transaction;
		}
		
	return result;
	}
	
TInt CSignallingChannel::SendGetCapabilities(XAvdtpSignalReceiver& aReceiver, TSEID aACPSEID)
	{
	LOG_FUNC
	if (!aACPSEID.IsValid() || aACPSEID.IsLocal())
		{
		return KErrArgument;
		}
	if (!iBearer)
		{
		return KErrNotReady;
		}

	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpGetCapabilities);
	TInt result = KErrNoMemory;

	if (transaction)
		{
		result = AvdtpSignallingMessageGetCapabilities::Command::Format(transaction->Message(), aACPSEID);
		if (result==KErrNone)
			{
			//Stuff the seid in the CSignallingTransaction as AVDTP spec forgets it
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));			
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
	return result;
	}
	
TInt CSignallingChannel::SendReleaseAccept(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
    // want to turn this around asap to avoid remote hanging, and races with local l2cap shutting down
    // so one-shot bump the priority

 	if(!iTryToSendCallback->IsActive())
 	    {
		iTryToSendCallback->SetPriority(KAvdtpReleaseAcceptPriority);	
    	}
    else if(iTryToSendCallback->Priority()!=KAvdtpReleaseAcceptPriority)
   		{
   		//Callback is active trying to send a low priority item
   		//Cancel it and set the priority to high. The priority will remain high until the
   		//callback has run enough times to empty the message queue.
   		iTryToSendCallback->Cancel();
    	iTryToSendCallback->SetPriority(KAvdtpReleaseAcceptPriority);
    	}
    //Else, do nothing. The priority will remain high until the message queue has been
    //cleared, so the EAvdtpRelease about to be added should be sent whilst the priority
    //is still high
    	
    return SendAccept(aLabel, EAvdtpRelease);
	}
	
	
TInt CSignallingChannel::SendSetConfigurationAccept(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	return SendAccept(aLabel, EAvdtpSetConfiguration);
	}
									  
TInt CSignallingChannel::SendSetConfigurationReject(TAvdtpTransactionLabel aLabel,
											TBluetoothAvDistributionError aResult,
									  		TAvdtpServiceCategory aCategory)
	{
	LOG_FUNC
	// packet size of errored category is 8bits
	TPckg<TUint8> cat(static_cast<TUint8>(aCategory));
	return SendReject(aLabel, EAvdtpSetConfiguration, aResult, &cat);
	}

TInt CSignallingChannel::SendReconfigureAccept(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	return SendAccept(aLabel, EAvdtpReconfigure);
	}
	
TInt CSignallingChannel::SendReconfigureReject(TAvdtpTransactionLabel aLabel,
											TBluetoothAvDistributionError aResult,
									  		TAvdtpServiceCategory aCategory)
	{
	LOG_FUNC
	// packet size of errored category is 8bits
	TPckg<TUint8> cat(static_cast<TUint8>(aCategory));
	return SendReject(aLabel, EAvdtpReconfigure, aResult, &cat);
	}

TInt CSignallingChannel::SendSecurityControlAccept(TAvdtpTransactionLabel aLabel, const TDesC8* aOptionalData)
	{
	LOG_FUNC
	return SendAccept(aLabel, EAvdtpSecurityControl, aOptionalData);	
	}
	
TInt CSignallingChannel::SendSecurityControlReject(TAvdtpTransactionLabel aLabel, TBluetoothAvDistributionError aResult)
	{
	LOG_FUNC
	return SendReject(aLabel, EAvdtpSecurityControl, aResult, NULL);
	}
	
TInt CSignallingChannel::SendRelease(XAvdtpSignalReceiver& aReceiver, TSEID aACPSEID)
	{
	LOG_FUNC
	if (!aACPSEID.IsValid())
		{
		return KErrArgument;
		}
	if (!iBearer)
		{
		return KErrNotReady;
		}

	// create transaction
	TInt result = KErrNoMemory;
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpRelease);
	
	if (transaction)
		{
		result = AvdtpSignallingMessageStreamRelease::Command::Format(transaction->Message(), aACPSEID);
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
			
	// create packet
	return result;	
	}
	
TInt CSignallingChannel::SendSetConfiguration(XAvdtpSignalReceiver& aReceiver,
										  TSEID aINTSEID,
										  TSEID aACPSEID,
										  const RBuf8& aConfiguration)
	{
	LOG_FUNC
	if (!aACPSEID.IsValid() || !aINTSEID.IsValid())
		{
		return KErrArgument;
		}

	if (!iBearer)
		{
		return KErrNotReady;
		}

	// create transaction
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpSetConfiguration);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result = AvdtpSignallingMessageSetConfiguration::Command::Format(transaction->Message(), aACPSEID, aINTSEID, aConfiguration);
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
			
	// create packet
	return result;	
	}


TInt CSignallingChannel::SendReconfigure(XAvdtpSignalReceiver& aReceiver,
										  TSEID aACPSEID,
										  const RBuf8& aConfiguration)
	{
	LOG_FUNC
	if (!aACPSEID.IsValid())
		{
		return KErrArgument;
		}
	if (!iBearer)
		{
		return KErrNotReady;
		}

	// create transaction
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpReconfigure);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result = AvdtpSignallingMessageReconfigure::Command::Format(transaction->Message(), aACPSEID, aConfiguration);
		
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
			
	// create packet
	return result;	
	}


TInt CSignallingChannel::SendStartStream(XAvdtpSignalReceiver& aReceiver, TSEID aRemoteSEID)
	{
	LOG_FUNC
	if (!iBearer)
		{
		return KErrNotReady;
		}

	// create transaction
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpStart);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result = AvdtpSignallingMessageStart::Command::Format(transaction->Message(), aRemoteSEID);
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aRemoteSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
			
	// create packet
	return result;	
	}


TInt CSignallingChannel::SendSuspendStream(XAvdtpSignalReceiver& aReceiver, TSEID aACPSEID)
	{
	LOG_FUNC
	if (!iBearer)
		{
		return KErrNotReady;
		}

	// create transaction
	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpSuspend);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result=AvdtpSignallingMessageSuspend::Command::Format(transaction->Message(), aACPSEID);
		if (result==KErrNone)
			{
			transaction->SetCookie(reinterpret_cast<TAny*>(aACPSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
			
	// create packet
	return result;	
	}
		


TInt CSignallingChannel::SendAbort(XAvdtpSignalReceiver& aReceiver, TSEID aRemoteSEID)
	{
	LOG_FUNC
	if (!aRemoteSEID.IsValid())
		{
		return KErrArgument;
		}
	if (!iBearer)
		{
		return KErrNotReady;
		}

	CSignallingTransaction*	transaction = PrepareSignallingCommand(aReceiver, EAvdtpAbort);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result = AvdtpSignallingMessageAbort::Command::Format(transaction->Message(), aRemoteSEID);
		if (result==KErrNone)
			{
			//Stuff the seid in the CSignallingTransaction as AVDTP forgets it
			transaction->SetCookie(reinterpret_cast<TAny*>(aRemoteSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
	return result;
	}
	

TInt CSignallingChannel::SendSecurityControl(XAvdtpSignalReceiver& aReceiver, 
									    TSEID aRemoteSEID,
									    const TDesC8& aSecurityData)
	{
	LOG_FUNC
	if (!aRemoteSEID.IsValid())
		{
		return KErrArgument;
		}
	if (!iBearer)
		{
		return KErrNotReady;
		}

	CSignallingTransaction* transaction = PrepareSignallingCommand(aReceiver, EAvdtpSecurityControl);
	TInt result = KErrNoMemory;
	
	if (transaction)
		{
		result = AvdtpSignallingMessageSecurityControl::Command::Format(transaction->Message(), aRemoteSEID, aSecurityData);
			
		if (result==KErrNone)
			{
			//Stuff the seid in the CSignallingTransaction as AVDTP spec forgets it
			transaction->SetCookie(reinterpret_cast<TAny*>(aRemoteSEID.Value()));
			result=EnqueueMessage(*transaction);
			}
		if (result!=KErrNone)
			{
			delete transaction;
			}
		}
		
	return result;
	}
	
	
TInt CSignallingChannel::SendStartAccept(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	return SendAccept(aLabel, EAvdtpStart);
	}
	
TInt CSignallingChannel::SendStartReject(TAvdtpTransactionLabel aLabel,
											TBluetoothAvDistributionError aResult,
											TSEID aBadSEID)
	{
	LOG_FUNC
	TPckg<TUint8> badSEID(aBadSEID.PacketValue());
	return SendReject(aLabel, EAvdtpStart, aResult, &badSEID);
	}
	
TInt CSignallingChannel::SendSuspendAccept(TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	return SendAccept(aLabel, EAvdtpSuspend);
	}

TInt CSignallingChannel::SendSuspendReject(TAvdtpTransactionLabel aLabel,
											TBluetoothAvDistributionError aResult,
											TSEID aBadSEID)
	{
	LOG_FUNC
	TPckg<TUint8> badSEID(aBadSEID.PacketValue());
	return SendReject(aLabel, EAvdtpSuspend, aResult, &badSEID);
	}
	
/**
Places a message (defined by the transaction) into the outbound queue
At this point the transaction label for commands is determined
Certain commands (such as Abort) may be placed at the head of the queue - it is this function that decides
If a packet is added to the queue, and the bearer is not blocked, then async send callback is kicked
@internalComponent
**/
TInt CSignallingChannel::EnqueueMessage(CSignallingTransaction& aTransaction)
	{
	LOG_FUNC
	// from transaction get the message
	CAvdtpOutboundSignallingMessage& message = aTransaction.Message();
	
	TInt result=KErrNone;

	//OutBound Signalling message should never be EReserved
	__ASSERT_DEBUG(message.iSignal!= EReserved,Panic(EAvdtpInvalidReservedValueInOutboundSignallingMessage));
	if (message.iMessageType==ECommand)
		{
		result = message.AllocateTransactionLabel(iLabelGen);
		LOG1(_L("Allocated label %d"), message.TransactionLabel());
		}
	
	// remove from draft que, whether error occurred or not
	message.Deque(); 					
	
	if (result==KErrNone)
		{
		// remember the packet label in the transaction object
		aTransaction.SetLabel(message.TransactionLabel());
		// ok to send, figure out priority		
		if (aTransaction.Signal()==EAvdtpAbort)
			{
			// add to outbound queue (high priority)
			iQueuedMessages.AddFirst(message);
			}
		else
			{
			// add to outbound queue	
			iQueuedMessages.AddLast(message);
			}
		// kick off sending
		if (!Blocked())
			{
			StartTryToSendCallback();
			}	
		}
	
	return result;	
	}

/**
Creates a reponse packet
@param aMessageType whether accept or reject
@param aMessage the signalling response to be put in the packet
@param aLabel the transaction label - typically that used in the inbound command
@internalComponent
**/
CSignallingTransaction* CSignallingChannel::PrepareSignallingResponse(
												TAvdtpMessageType aMessageType,
												TAvdtpMessage aMessage,
												TAvdtpTransactionLabel aLabel)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = PrepareSignallingPacket(aMessageType, aMessage);
	if (transaction)
		{		
		transaction->Message().SetTransactionLabel(aLabel);
		}
	return transaction;
	}

/**
Creates a command packet and returns a transaction to the caller
@param aReceiver a reference to the caller - used for determining who sent the packet
@param aMessage the signalling command to be put in the packet
@internalComponent
**/
CSignallingTransaction* CSignallingChannel::PrepareSignallingCommand(
												XAvdtpSignalReceiver& aReceiver,
												TAvdtpMessage aMessage)
	{
	LOG_FUNC
	CSignallingTransaction* transaction = PrepareSignallingPacket(ECommand, aMessage);
	if (transaction)
		{
		transaction->SetUser(&aReceiver);
		}
	return transaction;
	}


/*
common private method
ownership not returned - transaction remains in signalling channel transaction queue
@internalComponent
*/
CSignallingTransaction* CSignallingChannel::PrepareSignallingPacket(TAvdtpMessageType aMessageType,
													TAvdtpMessage aMessage)
	{
	LOG_FUNC
	//todo: KErrInUse if SAP already preparing a message?
	CSignallingTransaction* transaction = CSignallingTransaction::New(*this,
												aMessage,
												aMessageType);
	
	if (transaction)
		{
		// until the packet is finalised, set to be on draft message queue
		iDraftMessages.AddLast(transaction->Message());
		iTransactions.AddLast(*transaction);
	
		// see what post-send action is required (timer etc - see GAVDP spec)
		transaction->SetSentAction();
		}
	return transaction;
	}

/*
Helper that factors similarity between SetConfiguration and Reconfigure confirms
@param aCategory this may be meaningless if aResult==KErrNone
@internalComponent
*/
void CSignallingChannel::ConfigConfirm(TAvdtpTransactionLabel aLabel,
											  TInt aResult,
											  TAvdtpServiceCategory aCategory,
											  TBool aReconfigure)
	{	
	LOG_FUNC
	CSignallingTransaction* transaction = FindTransaction(aLabel);
	RETURN_IF_NO_TRANSACTION(transaction);
	RETURN_IF_SIGNAL_BAD(transaction, aReconfigure ? EAvdtpReconfigure : EAvdtpSetConfiguration);
	
	// get acp seid from cookie
	TSEID seid(reinterpret_cast<TUint>(transaction->Cookie()));
	
	if (!aReconfigure)
		{
		transaction->User()->SetConfigConfirm(aResult, seid, aCategory);
   		// return label back to pool
   		// for error the setconfigconfirm will destroy the stream which in turn clears the transaction already
   		// don't want to try a double clean up - it doesn't work well :-)
 		// It is possible that SetConfigConfirm was an error and causes stream release
 		// which deletes all the streams transactions - including the one we
 		// just found. To guard against that check it exists (again)
 		transaction = FindTransaction(aLabel);
 		if ((transaction) && (aResult == KErrNone))
			{
			RemoveTransaction(*transaction); //clears RTX timer
			}
		}
	else
		{
		transaction->User()->ReconfigConfirm(aResult, seid, aCategory);
		// return label back to pool
		RemoveTransaction(*transaction); //clears RTX timer
		}	
	}


/**
Sends a rejection response
The response can be to any of the signalling messages.
@param aError The error code to place in the packet
@param aRejectionData Trailing extended error information
@internalComponent
*/
TInt CSignallingChannel::SendReject(TAvdtpTransactionLabel aLabel,
									TAvdtpMessage aMessage,
									TBluetoothAvDistributionError aError,
									const TDesC8* aRejectionData)
	{
	LOG_FUNC
	__ASSERT_DEBUG(iBearer, Panic(EAvdtpSignallingChannelLogicalChannelNotReady));	
	__ASSERT_DEBUG(!(aMessage==EAvdtpDiscover && aError==EAvdtpBadACPSEID), Panic(EAvdtpBadErrorCase));
	
	if (!iBearer)
		{
		return KErrNotReady;
		}
	
	CSignallingTransaction* transaction = NULL;
	TInt result = KErrNone;
	
	//If its a valid signal identifier then send the 'Reject Response' otherwise send 'General Reject' 
	if (aMessage < EAvdtpLargestValidSignalIndentifier)
		{
		transaction = PrepareSignallingResponse(EResponseReject, aMessage, aLabel);	
		// now need to append more data for certain packets													 
		switch (aMessage)
			{
			// those that just have error code
			case EAvdtpDiscover:
			case EAvdtpGetCapabilities:
			case EAvdtpGetConfiguration:
			case EAvdtpOpen:
			case EAvdtpRelease:
			case EAvdtpSecurityControl:
				{
				__ASSERT_DEBUG(aRejectionData == NULL, Panic(EAvdtpSignallingMessageResponseNoTrailingDataExpected));
				result = transaction ? AvdtpSignallingMessage::Reject::Format(transaction->Message(), aError) : KErrNoMemory;
				}
			break;
			
			// those with extra rejection info
			case EAvdtpSetConfiguration:
			case EAvdtpReconfigure:
			case EAvdtpSuspend:
			case EAvdtpStart:
				{
				__ASSERT_DEBUG(aRejectionData != NULL, Panic(EAvdtpSignallingMessageResponseTrailingDataExpected));
				// need to append data to the packet (SEID etc)
				result = transaction ? AvdtpSignallingMessage::Reject::Format(transaction->Message(), aError, aRejectionData) : KErrNoMemory;
				}
			break;
			default:
				break;			// unsupported, just leave to time out
			}
		}
	else
		{
		__ASSERT_DEBUG(aRejectionData == NULL, Panic(EAvdtpSignallingMessageResponseNoTrailingDataExpected));
		//For General Reject send the invalid signal identifier which is causing this general reject	
		transaction = PrepareSignallingResponse(EGeneralReject, aMessage, aLabel);
		}

	if (!transaction)
		{
		result = KErrNoMemory;
		}

	if (result==KErrNone)
		{
		(void)EnqueueMessage(*transaction);
		}
	else
		{
		delete transaction;
		}
	return result;
	}

TInt CSignallingChannel::SendAccept(TAvdtpTransactionLabel aLabel, TAvdtpMessage aMessage, const TDesC8* aOptionalData /*= NULL*/)
	{
	LOG_FUNC
	if (!iBearer)
		{
		return KErrNotReady;
		}
	
	TInt result = KErrNone;
	CSignallingTransaction*	transaction = PrepareSignallingResponse(EResponseAccept, aMessage, aLabel);
	if (transaction)
		{
		if (aOptionalData)
			{
			// no other messages have data in accept in avdtp 1.0
			__ASSERT_DEBUG(aMessage==EAvdtpSecurityControl, Panic(EAvdtpSignallingMessageResponseNoTrailingDataExpected));
			result = AvdtpSignallingMessageSecurityControl::Accept::Format(transaction->Message(), aOptionalData);
			}
		}
	else
		{
		result = KErrNoMemory;
		}
	
	if (result == KErrNone)
		{
		result=EnqueueMessage(*transaction);
		}
	else
		{
		delete transaction;
		}
		
	return result;	
	}

TBool CSignallingChannel::CheckSignal(const CSignallingTransaction& aTransaction, TAvdtpMessage aSignal) const
	{
	LOG_FUNC
	return aTransaction.Signal() == aSignal;
	}