diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avdtp/avdtpSignallingChannel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avdtp/avdtpSignallingChannel.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1903 @@ +// 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 +#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(aSigCh))->TryToSendCallback(); + } + +void CSignallingChannel::ObtainMTU() + { + LOG_FUNC + TPckg 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 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 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 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(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 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 iter(iPermanentUsers); + while (iter) + { + iter++->SignallingChannelError(aError); + } + } + +void CSignallingChannel::ErrorServiceRequesters(TInt aError) + { + LOG_FUNC + // complete all outstanding services + TDblQueIter 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 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 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 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(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(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 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(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 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 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(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(aACPSEID.Value())); + result=EnqueueMessage(*transaction); + } + if (result!=KErrNone) + { + delete transaction; + } + } + return result; + } + +void CSignallingChannel::OpenIndication(TAvdtpTransactionLabel aLabel, TSEID aSEID) + { + LOG_FUNC + TDblQueIter 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(transaction->Cookie()))); + RemoveTransaction(*transaction); //clears RTX timer + } + + +void CSignallingChannel::StartIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) + { + LOG_FUNC + TDblQueIter 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 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(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(transaction->Cookie()))); + RemoveTransaction(*transaction); //clears RTX timer + } + + +void CSignallingChannel::ReleaseIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) + { + LOG_FUNC + TDblQueIter 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(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 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 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 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(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 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(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(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(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 cat(static_cast(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 cat(static_cast(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(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(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(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(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(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(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(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 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 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(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; + } + +