// 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 session// ///** @file @internalComponent*/#include <bluetooth/logger.h>#include "avdtpSignallingSession.h"#include "avdtpSignallingChannel.h"#include "avdtp.h"#include "avdtpsap.h"#include "avdtpStream.h"#include "avdtpLogicalChannelFactory.h"#include "avdtpConfigurators.h"#include "avdtputil.h"#include "avdtpDirectChannel.h"#ifdef __FLOG_ACTIVE_LIT8(KLogComponent, LOG_COMPONENT_AVDTP);#endif#ifdef _DEBUGPANICCATEGORY("avdtpsigse");#endifCSignallingSession* CSignallingSession::NewLC(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP) { LOG_STATIC_FUNC CSignallingSession* s = new (ELeave) CSignallingSession(aProtocol, aSAP); CleanupStack::PushL(s); s->ConstructL(); return s; }CSignallingSession* CSignallingSession::NewL(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP) { LOG_STATIC_FUNC CSignallingSession* s = CSignallingSession::NewLC(aProtocol, aSAP); CleanupStack::Pop(); return s; }CSignallingSession::CSignallingSession(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP): CTransportSession(aProtocol, aSAP, ESignalling), iLocalSEPs(_FOFF(CLocalSEP, iSignallingSessionLink)) { LOG_FUNC }CSignallingSession::~CSignallingSession() { LOG_FUNC DestroyLocalSEPs(); ClearSignallingChannel(); delete iSEPConfigurator; }void CSignallingSession::ConstructL() { LOG_FUNC CTransportSession::ConstructL(); }TInt CSignallingSession::ActiveOpen() { LOG_FUNC // esock will protect us from an accidental double connect TInt ret = KErrNone; if (iSignallingChannel) { if (!iSignallingChannel->IsListening()) { // already got a suitable SC - probably due to a passive connection // just say it's done (we're already attached) iSAP.Ready(); } else { // signalling channel is listening - detach from it, user has decided to connect // drop through afterwards ClearSignallingChannel(); } } // not else as ClearSignallingChannel will NULL iSignallingChannel if (!iSignallingChannel) { iSignallingChannel = iProtocol.GetSignallingChannel(RemoteAddress()); if (iSignallingChannel) { ret = iSignallingChannel->AttachSignallingUser(*this); } else { ret = KErrNoMemory; } } return ret; }void CSignallingSession::ClearSignallingChannel() { LOG_FUNC if (iSignallingChannel) { iSignallingChannel->DetachSignallingUser(*this); } iSignallingChannel = NULL; }void CSignallingSession::DestroyLocalSEPs() { LOG_FUNC while (!iLocalSEPs.IsEmpty()) { CLocalSEP* sep = iLocalSEPs.First(); sep->iSignallingSessionLink.Deque(); // inform codman - it'll clear bits as necessary since it reference coutns iProtocol.CodMan().RemoveCodService(sep->Info().IsSink() ? EMajorServiceRendering : EMajorServiceCapturing); delete sep; } }void CSignallingSession::DoShutdown() { LOG_FUNC TDblQueIter<CLocalSEP> iter(iLocalSEPs); while(iter) { iter++->SetInUse(EFalse); } ClearSignallingChannel(); if (iSAPShutdown == ENormal) { iSAP.CanClose(); } else { // base class has detached us so that we own ourselves // nothing more to do, apart from die. delete this; } }TInt CSignallingSession::Send(RMBufChain& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) { LOG_FUNC // not supported in signalling - only ioctls and opts allowed return 0; }TInt CSignallingSession::GetData(RMBufChain& /*aData*/) { LOG_FUNC return KErrNotSupported; }// stuff forwarded from SAPTInt CSignallingSession::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption) { LOG_FUNC TInt ret = KErrNotSupported; if (aLevel == KSolBtAVDTPSignalling) { switch (aName) { case EAwaitPassiveSignallingChannel: { if (iIsListening && iSignallingChannel && iSignallingChannel->IsListening()) { ret = KErrAlreadyExists; } else if (iSignallingChannel && !iSignallingChannel->IsListening()) { ret = KErrInUse; } else { iSignallingChannel=iProtocol.FindListeningSignallingChannel(); // For ACP - triggered when client is ready for listening // (would be nice to be "automatic", but needs client to be happy they've registered all their seps if (!iSignallingChannel) { iSignallingChannel = iProtocol.CreateSignallingChannelForListening(); } if (iSignallingChannel) { ASSERT_DEBUG(iSignallingChannel->IsListening()); ret = iSignallingChannel->AttachSignallingUser(*this); if (ret == KErrNone) { iIsListening = ETrue; } else { // if we didn't manage to tell anyone else // then no one else knows so actually we're best // off cleaning up after ourselves. iSignallingChannel->DetachSignallingUser(*this); iSignallingChannel = NULL; } } else { ret = KErrNoMemory; } } break; } case EStopAwaitPassiveSignallingChannel: { if (!iSignallingChannel) { ret = KErrNotReady; } else { iIsListening = EFalse; ClearSignallingChannel(); } break; } // also for reconfigure - the configurator decides case EStartConfiguringRemote: { if (iSEPConfigurator) { ret = KErrInUse; } else { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TInternalSelectRemoteSEP)) { ret = KErrBadDescriptor; } else { // get Remote SEID we're configuring, and the binding local SEP SEID // avdtp spec makes us have to do the binding now TInternalSelectRemoteSEP select = *reinterpret_cast<const TInternalSelectRemoteSEP*>(aOption.Ptr()); if (!select.iLocalSEID.IsLocal()) { ret = KErrArgument; } else { CLocalSEP* localSEP = FindLocalSEP(select.iLocalSEID); if (!localSEP) { // client supplied bad SEID ret = KErrArgument; } else { // don't support reconfiguring of an active stream - client must suspend CAVStream* stream = localSEP->Stream(); if (stream && stream->IsStreaming()) { ret = KErrInUse; } else { TRAP(ret, iSEPConfigurator = CRemoteSEPConfigurator::NewL(*this, iProtocol, select.iLocalSEID, select.iRemoteSEID, stream ? ETrue : EFalse)); } } } } } break; } case EStartConfiguringLocal: { if (iSEPConfigurator) { ret = KErrInUse; } else { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TSEID)) { ret = KErrBadDescriptor; } else { // get Local SEID we're configuring TSEID localseid = *reinterpret_cast<const TSEID*>(aOption.Ptr()); TRAP(ret, iSEPConfigurator = CLocalSEPConfigurator::NewL(*this, iProtocol, localseid)); } } break; } case EAddCapabilitySelection: { if (iSEPConfigurator) { ret = iSEPConfigurator->AddCapability(aOption); } else { ret = KErrNotReady; } break; } case ESendConfigurationResponse: { if (iSignallingChannel) { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TAvdtpInternalConfigurationResponse)) { ret = KErrBadDescriptor; } else { const TAvdtpInternalConfigurationResponse rsp = *reinterpret_cast<const TAvdtpInternalConfigurationResponse*>(aOption.Ptr()); // check SEID is ok CLocalSEP* sep = FindLocalSEP(rsp.iSEID); if (sep) { // need to tell sep of the outcome so it can clear its state // this needs to occur for config and reconfig // first param is true if result is kerrnone- i.e. all ok, false otherwise TRAP(ret, sep->CommitPendingConfigurationL(rsp.iResult == KErrNone,rsp.iIsReconfigure)); if (!rsp.iIsReconfigure) { if (rsp.iResult == KErrNone) { if (ret==KErrNone) { ret = iSignallingChannel->SendSetConfigurationAccept(rsp.iTransactionLabel); if (ret==KErrNone) { // time to spawn that (ACP) stream TRAP(ret, sep->ExpectStreamL(RemoteAddress(), *this, iProtocol)); } } } else { ret = iSignallingChannel->SendSetConfigurationReject(rsp.iTransactionLabel, AvdtpInternalUtils::SymbianErrorToAvdtpError(rsp.iResult), rsp.iCategory); } } else { if (!sep->InUse()) { // almost assertable since we shouldnt have forwarded the indication if not // but we don't want a DoS if bad client ret = KErrNotReady; } else { if (rsp.iResult == KErrNone) { ret = iSignallingChannel->SendReconfigureAccept(rsp.iTransactionLabel); } else { ret = iSignallingChannel->SendReconfigureReject(rsp.iTransactionLabel, AvdtpInternalUtils::SymbianErrorToAvdtpError(rsp.iResult), rsp.iCategory); } } } if (ret==KErrNone && rsp.iResult == KErrNone && !rsp.iIsReconfigure) { // now in use, and from AVDTP spec sect6.3 we now do not expect other transactions until stream started // not point resetting for reconfigure sep->SetInUse(ETrue); } } else { ret = KErrArgument; } } } else { ret = KErrDisconnected; } break; } case ESendStartResponse: { if (iSignallingChannel) { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TAvdtpInternalStartResponse)) { ret = KErrBadDescriptor; } else { const TAvdtpInternalStartResponse rsp = *reinterpret_cast<const TAvdtpInternalStartResponse*>(aOption.Ptr()); // check SEID is ok CLocalSEP* sep = FindLocalSEP(rsp.iSEID); if (sep && sep->Stream() && rsp.iResult == KErrNone) { ret = iSignallingChannel->SendStartAccept(rsp.iTransactionLabel); if (ret==KErrNone) { sep->Stream()->Started(); } } else if (sep && !(sep->Stream())) { ret = KErrUnknown; } else if (sep) { // The AVDTP error code (spec... ACP to INT error code table) is offset by -18045 ret = iSignallingChannel->SendStartReject(rsp.iTransactionLabel, AvdtpInternalUtils::SymbianErrorToAvdtpError(rsp.iResult), rsp.iSEID); } else { ret = KErrArgument; } } } else { ret = KErrDisconnected; } break; } case ESendSuspendResponse: { if (iSignallingChannel) { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TAvdtpInternalSuspendResponse)) { ret = KErrBadDescriptor; } else { const TAvdtpInternalSuspendResponse rsp = *reinterpret_cast<const TAvdtpInternalSuspendResponse*>(aOption.Ptr()); // check SEID is ok CLocalSEP* sep = FindLocalSEP(rsp.iSEID); if (sep && sep->Stream() && rsp.iResult == KErrNone) { ret = iSignallingChannel->SendSuspendAccept(rsp.iTransactionLabel); if (ret==KErrNone) { sep->Stream()->Suspended(); } } else if (sep && !(sep->Stream())) { ret = KErrUnknown; } else if (sep) { // The AVDTP error code (spec... ACP to INT error code table) is offset by -18045 ret = iSignallingChannel->SendSuspendReject(rsp.iTransactionLabel, AvdtpInternalUtils::SymbianErrorToAvdtpError(rsp.iResult), rsp.iSEID); } else { ret = KErrArgument; } } } else { ret = KErrDisconnected; } break; } case ESendSecurityControlResponse: { if (iSignallingChannel) { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TAvdtpInternalSecurityControlResponse)) { ret = KErrBadDescriptor; } else { const TAvdtpInternalSecurityControlResponse secRsp = *reinterpret_cast<const TAvdtpInternalSecurityControlResponse*>(aOption.Ptr()); // check SEID is ok CLocalSEP* sep = FindLocalSEP(secRsp.iSEID); if (!sep) { ret = KErrArgument; } else { if(secRsp.iResult == KErrNone) { const TDesC8* rspInfo = secRsp.iSecurityControlInfo.Length() ? &secRsp.iSecurityControlInfo : NULL; ret = iSignallingChannel->SendSecurityControlAccept(secRsp.iTransactionLabel, rspInfo); } else { ret = iSignallingChannel->SendSecurityControlReject(secRsp.iTransactionLabel, AvdtpInternalUtils::SymbianErrorToAvdtpError(secRsp.iResult)); } } } } else { ret = KErrDisconnected; } break; } case EShutdown: { ret=KErrNone; TDblQueIter<CLocalSEP> iter(iLocalSEPs); while(iter) { if (iter++->Stream()) { ret=KErrNotReady; } } if (ret==KErrNone) { iter.SetToFirst(); while(iter) { iter++->SetInUse(EFalse); } ClearSignallingChannel(); iSAP.SessionDisconnect(); } break; } } } return ret; }void CSignallingSession::Ioctl(TUint aLevel, TUint aName, const TDesC8* aOption) { LOG_FUNC TInt res = KErrNotSupported; if (aLevel == KSolBtAVDTPSignalling) { const TUint8* optionPtr=NULL; if (aOption) { optionPtr = aOption->Ptr(); } switch (aName) { case EDiscoverSEPs: { res=iSignallingChannel ? iSignallingChannel->SendDiscoverSEPs(*this) : KErrNotReady; break; } case EGetCapabilities: { // Check the integrity of the descriptor if (!optionPtr || aOption->Length() != sizeof(TSEID)) { res = KErrBadDescriptor; } else { TSEID seid = *reinterpret_cast<const TSEID*>(optionPtr); if (seid.IsLocal()) { // meaningless to try to associate with a remote sep at this stage // getting local capabilities not supported res = KErrArgument; } else { // client doesn't need to have populated remote sep cache at this point, so just try to send res=iSignallingChannel ? iSignallingChannel->SendGetCapabilities(*this, seid) : KErrNotReady; } } break; } case EAbortStream: { // Check the integrity of the descriptor if (!optionPtr || aOption->Length() != sizeof(TSEID)) { res = KErrBadDescriptor; } else { res = KErrNone; // check seid exists const TSEID& seid = *reinterpret_cast<const TSEID*>(optionPtr); TSEID packetSeid; if (seid.IsLocal()) { CLocalSEP* sep = FindLocalSEP(seid); // check local sep exists if (!sep || !(sep->Stream())) { res = KErrArgument; } else { packetSeid = sep->Stream()->RemoteSEID(); } } else { // remote was specified - again, check it'ok if (!FindStreamFromRemoteSEID(seid)) { res = KErrArgument; } else { packetSeid = seid; } } if (res==KErrNone) { res = SendAbort(packetSeid); } } break; } case ESelectSEP: { if (iSEPConfigurator) { TInt cfgRes = iSEPConfigurator->Finalise(); // user doesn't need to know whether we sync or async res = cfgRes == KRequestPending ? KErrNone : cfgRes; if (cfgRes==KErrNone) { // can complete the service now iSAP.ServiceComplete(NULL); } delete iSEPConfigurator; iSEPConfigurator = NULL; } else { res = KErrNotReady; } break; } case EStartStreaming: { if (iSignallingChannel==NULL) { res = KErrNotReady; } else { // Check the integrity of the descriptor if (!optionPtr || aOption->Length() != sizeof(TSEID)) { res = KErrBadDescriptor; } else { const TSEID& clientSeid = *reinterpret_cast<const TSEID*>(optionPtr); // see whether it was a local or remote seid - either is ok CAVStream* stream = NULL; if (clientSeid.IsLocal()) { CLocalSEP* sep = FindLocalSEP(clientSeid); if (sep) { stream = sep->Stream(); } } else { // client supplied remote seid so just check stream = FindStreamFromRemoteSEID(clientSeid); } res = stream ? stream->Start() : KErrUnknown; } } break; } case ESuspendStreaming: { if (iSignallingChannel==NULL) { res = KErrNotReady; } else { // Check the integrity of the descriptor if (!optionPtr || aOption->Length() != sizeof(TSEID)) { res = KErrBadDescriptor; } else { const TSEID& clientSeid = *reinterpret_cast<const TSEID*>(optionPtr); // see whether it was a local or remote seid - either is ok CAVStream* stream = NULL; if (clientSeid.IsLocal()) { CLocalSEP* sep = FindLocalSEP(clientSeid); if (sep) { stream = sep->Stream(); } } else { // client supplied remote seid so just check stream = FindStreamFromRemoteSEID(clientSeid); } res = stream ? stream->Suspend() : KErrUnknown; } } break; } case ESendSecurityControl: { //GAVDP is wishing to send security control command (allowed any time if capable) // needs to be remote // Check the integrity of the descriptor if (!optionPtr || aOption->Length() != sizeof(TAvdtpInternalSendSecurityControl)) { res = KErrBadDescriptor; } else { const TAvdtpInternalSendSecurityControl& security = *reinterpret_cast<const TAvdtpInternalSendSecurityControl*>(optionPtr); if (!security.iRemoteSEID.IsLocal()) { // check capability if (iSignallingChannel) { if (iProtocol.RemoteSEPCache().HasCapability(RemoteAddress(), security.iRemoteSEID, EServiceCategoryContentProtection)) { res = iSignallingChannel->SendSecurityControl(*this, security.iRemoteSEID, security.iSecurityControlInfo); } else { // remote doesnt support CP res = KErrNotSupported; } } else { res = KErrNotReady; } } else { res = KErrArgument; } } break; } default: { res = KErrNotSupported; } } if (res!=KErrNone) { iSAP.Error(res); } } }#ifdef _DEBUG void CSignallingSession::CancelIoctl(TUint aLevel, TUint /*aName*/)#elsevoid CSignallingSession::CancelIoctl(TUint /*aLevel*/, TUint /*aName*/)#endif { LOG_FUNC __ASSERT_DEBUG(aLevel==KSolBtAVDTPSignalling, Panic(EAvdtpBadIoctl)); // none at present }TInt CSignallingSession::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const { LOG_FUNC TInt ret = KErrNotSupported; if (aLevel == KSolBtAVDTPSignalling) { switch (aName) { case EGetProposedConfiguration: { // Check the integrity of the descriptor. The option length is variable, subject to the payload length. if (!aOption.Ptr() || aOption.Length() < sizeof(TInternalGetProposedConfiguration)) { ret = KErrBadDescriptor; } else { TInternalGetProposedConfiguration& conf = *const_cast<TInternalGetProposedConfiguration*>(reinterpret_cast<const TInternalGetProposedConfiguration*>(aOption.Ptr())); CLocalSEP* s = FindLocalSEP(conf.iSEID); if (s) { // Check that the option buffer size is sufficient TPtrC8 pendingConf(s->PendingConfiguration()); if (aOption.MaxLength() < pendingConf.Length()) { ret = KErrUnderflow; } else { // would be nice to use a return struct // but gets tricky - we don't know how big the option is // so can't use TBuf. TPtr falls foul of IPC. aOption.Copy(pendingConf); ret = KErrNone; } } else { ret = KErrArgument; } } break; } case EGetSecurityControl: { // Check the integrity of the descriptor. The option length is variable, subject to the payload length. if (!aOption.Ptr() || aOption.Length() < sizeof(TInternalGetSecurityControl)) { ret = KErrBadDescriptor; } else { TInternalGetSecurityControl& sec = *const_cast<TInternalGetSecurityControl*>(reinterpret_cast<const TInternalGetSecurityControl*>(aOption.Ptr())); CLocalSEP* s = FindLocalSEP(sec.iSEID); if (s) { // Check that the option buffer size is sufficient TPtrC8 securityData(s->SecurityControl()); if (aOption.MaxLength() < securityData.Length()) { ret = KErrUnderflow; } else { aOption.Copy(securityData); ret = KErrNone; } } else { ret = KErrArgument; } } break; } case EGetAVDTPCapabilityResponse: { // Check the integrity of the descriptor. The option length is variable, subject to the payload length. if (!aOption.Ptr() || aOption.Length() < sizeof(TInternalGetSEPCapability)) { ret = KErrBadDescriptor; } else { TInternalGetSEPCapability cap = *reinterpret_cast<const TInternalGetSEPCapability*>(aOption.Ptr()); TPtrC8 ptr; // get the required capability from the SEP cache (in 'air' form) TRAP(ret, ptr.Set(iProtocol.RemoteSEPCache().GetCapabilityL( RemoteAddress(), cap.iSEID, cap.iServiceCategory));) if (ret==KErrNone) { // Check that the option buffer size is sufficient if (aOption.MaxLength() < ptr.Length()) { ret = KErrUnderflow; } else { aOption.Copy(ptr); } } } break; } case ERegisterSEP: { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TAvdtpSEPInfo)) { ret = KErrBadDescriptor; } else { TAvdtpSEPInfo info = *reinterpret_cast<const TAvdtpSEPInfo*>(aOption.Ptr()); // client doesn't manage inUse field - we do info.SetInUse(EFalse); TRAP(ret, const_cast<CSignallingSession*>(this)->CreateLocalSEPL(info)); TPckg<TAvdtpSEPInfo> pckg(info); aOption.Copy(pckg); } break; } case EGetReportingAndRecoveryConfig: { // Check the integrity of the descriptor if (!aOption.Ptr() || aOption.Length() != sizeof(TReportingAndRecovery)) { ret = KErrBadDescriptor; } else { TReportingAndRecovery rar; TPckg<TReportingAndRecovery> rarBuf(rar); rarBuf.Copy(aOption); CAVStream* stream = FindStreamFromRemoteSEID(rar.iSEID); if(stream == NULL) { ret = KErrNotFound; } else { rar.iReporting = stream->CheckConfigured(EServiceCategoryReporting); rar.iRecovery = stream->CheckConfigured(EServiceCategoryRecovery); aOption.Copy(rarBuf); ret = KErrNone; } } break; } } } return ret; }TInt CSignallingSession::OpenIndication(TSEID aSEID) { LOG_FUNC // now create the full Stream Object CLocalSEP* sep = FindLocalSEP(aSEID); if (sep && sep->Stream()) { // this will tell channel factory to expect n channels on sending resp TRAPD(err, sep->Stream()->AwaitLogicalChannelsL()); return err; } else if (sep && !sep->Stream()) { // the remote must have decided that a GetConfig yielded just what they wanted // and have proceeded directly with Open // this *violates* GAVDP - see p 16 return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); } else { return KErrNotFound; } }TInt CSignallingSession::DiscoverIndication(TAvdtpTransactionLabel /*aLabel*/, CAvdtpOutboundSignallingMessage& aDiscoverResponseMessage) { LOG_FUNC // iterate over all idle SEPs returning their capabilities // need to return the label back to the Signalling Channel to tie up response // this is *far* easier if we process this synchronously // otherwise the design is tricky - we'd need to call a primitive bacxk on SigCh // and it would need to know our last SEP info etc TDblQueIter<CLocalSEP> iter(iLocalSEPs); CLocalSEP* sep; while(iter) { sep = iter++; TRAPD(overFlow, aDiscoverResponseMessage.AddSEPInfoL(sep->Info())); if (overFlow) { // no more will go, try continuing with usecase though // so NOT releaving - cos the message is ok break; } } return KErrNone; }TInt CSignallingSession::GetCapsIndication(TAvdtpTransactionLabel /*aLabel*/, TSEID aSEID, CAvdtpOutboundSignallingMessage& aGetCapsResponseMessage) { LOG_FUNC // client has set caps now - if they haven't then we will prob just send empty stuff back // i.e. we send whatever we've got (but possibly "knowing" this fact // find the idle SEP with aSEID CLocalSEP* sep = FindLocalSEP(aSEID); // this uses the same pattern as :DiscoverIndication if (!sep) { return KErrNotFound; } TInt ret = KErrNone; for (TInt i=0; i<sep->Capabilities().Count(); i++) { // one at a time - easier for us to cope if we OOM if (sep->Capabilities()[i]) { // local sep has capability TRAPD(overFlow, aGetCapsResponseMessage.AddSEPCapabilityL(*sep->Capabilities()[i])); if (overFlow) { // no more will go // if even the first capability didn't go then we have to forget replying if (i==0) { // there are no caps in the response, so it's meaningless ret = KErrUnderflow; } // otherwise we can try continuing with usecase though // so NOT erroring - cos the message is ok break; } } } return ret; }TInt CSignallingSession::SetConfigIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID, TSEID aINTSEID, RBuf8& aConfig) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; }/*we check to see if the remote has (wrongly) set capabilities that the idle sep has notif the remote has then we reject the requestelse pass onto gavdp*/ CCapabilityNotAllowedVisitor* checker = new CCapabilityNotAllowedVisitor(sep->Categories()); if (!checker) { return KErrNoMemory; } checker->Process(aConfig); if (!checker->IsValid()) { // signalling channel expects us to put the duff category in the aConfig // take the first one (as suggested by spec) TPckgBuf<TAvdtpServiceCategory> catBuf(checker->WrongCategory()); aConfig.Copy(catBuf); delete checker; return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadServCategory); } delete checker; checker = NULL; // update the SEP to contain the pending configuration // commited when gavdp says ok TInt res = sep->SetPendingConfiguration(aConfig, EFalse); // transfers aConfig ownership if (res==KErrNone) { sep->SetRemoteSEID(aINTSEID); // all ok, so pass on an indication to GAVDP as they need to configure too TInternalAvdtpConfigurationIndication ind; ind.iIndication = EAvdtpSetConfiguration; ind.iPayloadLength = aConfig.Length(); ind.iSEID = aACPSEID; ind.iINTSEID = aINTSEID; ind.iTransactionLabel = aLabel; TPckg<TInternalAvdtpConfigurationIndication> indPckg(ind); iSAP.Indication(indPckg); } return res; }TInt CSignallingSession::GetConfigIndication(TAvdtpTransactionLabel /*aLabel*/, TSEID aLocalSEID, CAvdtpOutboundSignallingMessage& aGetConfigResponseMessage) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aLocalSEID); if (!sep) { return KErrNotFound; } // remote may not have sent SetConfiguration if (sep->PreConfigured()) { return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); } TInt ret = KErrNone; for (TInt i=0; i<sep->Configuration().Count(); i++) { // one at a time - easier for us to cope if we OOM if (sep->Configuration()[i]) { // local sep has capability TRAPD(overFlow, aGetConfigResponseMessage.AddSEPCapabilityL(*sep->Configuration()[i])); if (overFlow) { // no more will go // if even the first capability didn't go then we have to forget replying if (i==0) { // there are no caps in the response, so it's meaningless ret = KErrUnderflow; } // otherwise we can try continuing with usecase though // so NOT erroring - cos the message is ok break; } } } return ret; }TInt CSignallingSession::ReconfigIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID, RBuf8& aNewConfig) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } if (!sep->Stream()) { // remote has been bad return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); }/*we check to see if the remote has (wrongly) set capabilities that the spec forbidssee 8.10.1 avdtp sepcif the remote has then we reject the requestelse pass onto gavdp*/ TAvdtpServiceCategories allowed; allowed.SetCapability(EServiceCategoryMediaCodec); allowed.SetCapability(EServiceCategoryContentProtection); CCapabilityNotAllowedVisitor* checker = new CCapabilityNotAllowedVisitor(allowed); if (!checker) { return KErrNoMemory; } checker->Process(aNewConfig); if (!checker->IsValid()) { // signalling channel expects us to put the duff category in the aConfig // take the first one (as suggested by spec) TPckg<TUint8> catBuf(checker->WrongCategory()); aNewConfig.Copy(catBuf); delete checker; return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpInvalidCapabilities); } delete checker; checker = NULL;//take ownership of config buffer TInt res = sep->SetPendingConfiguration(aNewConfig, ETrue); if (res==KErrNone) { // all ok, so pass on an indication to GAVDP as they need to reconfigure too TInternalAvdtpConfigurationIndication ind; ind.iIndication = EAvdtpReconfigure; ind.iPayloadLength = aNewConfig.Length(); ind.iSEID = aACPSEID; // to give consistent API for Set and Re-configure , give the INT SEID again ind.iINTSEID = sep->Stream()->RemoteSEID(); ind.iTransactionLabel = aLabel; TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); } return res; }/** Handle a Start Indication from remote.The decision about when to issue the start indication to the client is taken bythe stream.@param aLabel The transaction label of the start command.@param aACPSEID The (local) seid referring to the stream to be started.*/TInt CSignallingSession::StartIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } TInt err = KErrUnknown; if (sep->Stream()) { // If the stream is in a valid state it will call back to the sig session // to cause the indication to be passed to the client. err = sep->Stream()->StartIndication(aLabel, aACPSEID); } if(err == KErrNone) { return KErrNone; } else { return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); } }TInt CSignallingSession::ReleaseIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } if (sep->Stream()) { // dont ask GAVDP to confirm // we also know that if we have a stream it must be releaseable (void)iSignallingChannel->SendReleaseAccept(aLabel); sep->SetInUse(EFalse); // We try and hand off the channels to be closed. This is because it is // the responsibility of the INT to release these so we can't just // shut them down straight away. We don't want to have to deal with the // complication of funny stream states and clearup by leaving them with the // stream once it's effectively dead. If it fails however, we just let the // channels go away. // This is only done for non-muxed streams, otherwise we just revert to // closing the channels ourselves. #ifndef __SYMBIAN_AVDTP_HIDE_MUX#warning ("Release ACP channel release must be fixed for muxed case")#endif if(!sep->MultiplexingConfigured()) { // Take the channels from the stream to disconnect // Note that although some of these may be the same CloseLogicalChannels will // deal with correctly closing each channel once. TFixedArray<CDirectChannel*, KAvdtpChannelArraySize> channels; channels[0] = static_cast<CDirectChannel*>(sep->Stream()->iMediaBinding.iChannel); channels[1] = static_cast<CDirectChannel*>(sep->Stream()->iReportingBinding.iChannel); channels[2] = static_cast<CDirectChannel*>(sep->Stream()->iRecoveryBinding.iChannel); TArray<CDirectChannel*> array = channels.Array(); TRAP_IGNORE(iProtocol.LogicalChannelFactory().CloseSessionLogicalChannelsL(array, KAvdtpACPReleaseChannelCloseTimeout)); } // just error the stream - it will tell the user-plane sessions // they'll then tell the user // so , like open, we don't let user directly see this as an indication DestroyStream(sep->Stream(), KErrDisconnected); // all ok, so pass on an indication to GAVDP TInternalAvdtpAirIndication ind; ind.iIndication = EAvdtpRelease; ind.iPayloadLength = 0; ind.iSEID = aACPSEID; ind.iTransactionLabel = aLabel; // not used TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); return KErrNone; } else { // remote is in bad state return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); } }TInt CSignallingSession::SuspendIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } // check that we think stream suspendable if (sep->Stream() && sep->Stream()->IsStreaming()) { // all ok, so pass on an indication to GAVDP TInternalAvdtpAirIndication ind; ind.iIndication = EAvdtpSuspend; ind.iPayloadLength = 0; ind.iSEID = aACPSEID; ind.iTransactionLabel = aLabel; TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); return KErrNone; } else { return SymbianBluetoothAV::ConvertToSymbianError::AvdtpError(EAvdtpBadState); } }TInt CSignallingSession::AbortIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } else { // tell the sep, stream, then the user (only if we have a stream though) sep->SetInUse(EFalse); if (sep->Stream()) { DestroyStream(sep->Stream(),KErrAbort); } // Pass on an indication to GAVDP as they need to know too TInternalAvdtpAirIndication ind; ind.iIndication = EAvdtpAbort; ind.iSEID = aACPSEID; ind.iTransactionLabel = aLabel; // not that useful, client cant respond TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); } return KErrNone; }void CSignallingSession::AbortConfirm(TSEID aRemoteSEID) { LOG_FUNC // tell client CompleteBasicService(KErrNone); // error stream associated with remote CAVStream* stream = FindStreamFromRemoteSEID(aRemoteSEID); if (stream) { DestroyStream(stream, KErrAbort); } }TInt CSignallingSession::SecurityControlIndication(TAvdtpTransactionLabel aLabel, TSEID aACPSEID, const HBufC8* aSecurityData) { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aACPSEID); if (!sep) { // for a SEP not on this session return KErrNotFound; } else { // pass ownership of HBuf to SEP sep->SetSecurityControl(aSecurityData); // all ok, so pass on an indication to GAVDP as they need to do the actual security control response TInternalAvdtpAirIndication ind; ind.iIndication = EAvdtpSecurityControl; ind.iSEID = aACPSEID; ind.iPayloadLength = aSecurityData->Length(); ind.iTransactionLabel = aLabel; TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); return KErrNone; } }void CSignallingSession::SecurityControlConfirm(TInt aResult, TSEID aRemoteSEID, const TDesC8& aResponseData) { LOG_FUNC if (aResult==KErrNone) { TPckgBuf<TAvdtpInternalSendSecurityControl> securityControlPckgBuf; securityControlPckgBuf().iRemoteSEID = aRemoteSEID; securityControlPckgBuf().iSecurityControlInfo = aResponseData; iSAP.ServiceComplete(&securityControlPckgBuf); } else { iSAP.Error(aResult); } }void CSignallingSession::DiscoverConfirm(TInt aResult, const TAvdtpInternalDiscoverConfirm* const aConfirm) { LOG_FUNC if (aResult==KErrNone) { __ASSERT_DEBUG(aConfirm, Panic(EAvdtpSignallingSessionReceivedBadIndication)); //complete the client request with the number of SEPs TPckgBuf<TAvdtpInternalDiscoverConfirm> buf(*aConfirm); iSAP.ServiceComplete(&buf); } else { //either we were rejected by the peer or there was an internal //error in the signalling channel iSAP.Error(aResult); } }void CSignallingSession::GetCapsConfirm(TInt aResult, TSEID /*aRemoteSEID*/, TAvdtpServiceCatBitMask aSeen) { LOG_FUNC if (aResult==KErrNone) { TPckgC<TAvdtpServiceCatBitMask> buf(aSeen); iSAP.ServiceComplete(&buf); } else { iSAP.Error(aResult); } }void CSignallingSession::StreamStarted(TSEID aLocalSEID) { LOG_FUNC if (FindLocalSEP(aLocalSEID)) { // now tell client CompleteBasicService(KErrNone); } }void CSignallingSession::StreamSuspended(TSEID aLocalSEID) { LOG_FUNC if (FindLocalSEP(aLocalSEID)) { // now tell client CompleteBasicService(KErrNone); } }void CSignallingSession::StreamAccepted(TSEID aLocalSEID, TSEID aRemoteSEID, TBool aWithReporting, TBool aWithRecovery) { LOG_FUNC __ASSERT_DEBUG(aLocalSEID.IsLocal(), Panic(EAvdtpSEIDHasWrongDomain)); TInternalAvdtpStreamReadyIndication ind; ind.iSEID = aLocalSEID; ind.iRemoteSEID = aRemoteSEID; ind.iReportingPresent = aWithReporting; ind.iRecoveryPresent = aWithRecovery; TPckg<TInternalAvdtpStreamReadyIndication> indPckg(ind); iSAP.Indication(indPckg); }void CSignallingSession::StreamConfigured(TSEID /*aLocalSEID*/, TInt aResult, TAvdtpServiceCategory aFailedCategory) { LOG_FUNC // tell the user in all cases TPckgBuf<TAvdtpConfigReject> rejDataBuf; rejDataBuf().iError = aResult; rejDataBuf().iFailedCategory = aFailedCategory; // GAVDP will call back to client with Error upcall, so no need to error SAP iSAP.ServiceComplete(&rejDataBuf); }/*This callback is for the stream to inform gavdp of a failure in its state machineThe signalling session SHALL NOT mutate the stream based on this; the stream shouldproceed, or die, or error in line with its present state and potential GAVDP recovery of that stream*/void CSignallingSession::StreamInitiatedServiceFailed(const CAVStream& /*aStream*/, TInt aError) { LOG_FUNC ServiceError(aError); }/** This should be called from the stream when it is ready for a start indicationto be passed up to the client. */ void CSignallingSession::StreamReadyForStartIndication(const TAvdtpTransactionLabel aLabel, const TSEID aSeid) { LOG_FUNC // pass on an indication to GAVDP TInternalAvdtpAirIndication ind; ind.iIndication = EAvdtpStart; ind.iPayloadLength = 0; ind.iSEID = aSeid; ind.iTransactionLabel = aLabel; TPckg<TInternalAvdtpAirIndication> indPckg(ind); iSAP.Indication(indPckg); }TInt CSignallingSession::SendAbort(TSEID aRemoteSEID) { LOG_FUNC return iSignallingChannel ? iSignallingChannel->SendAbort(*this, aRemoteSEID) : KErrNotReady; }/*inline*/void CSignallingSession::CompleteBasicService(TInt aErr) { LOG_FUNC // basic services have no data to return aErr ? iSAP.Error(aErr) : iSAP.ServiceComplete(NULL); }/**Called as an INT - create the Stream object when we're setting configuration*/void CSignallingSession::DoConfigureStreamL(RBuf8& aConfigBuffer, CLocalSEP& aLocalSEP, TSEID aRemoteSEID, TBool aReportingConfigured, TBool aRecoveryConfigured) { LOG_FUNC // check to see if SignallingChannel went down during configuration if (iSignallingChannel) { TBool streamCreated = EFalse; // if there is no stream then we create one now // a stream would exist if this is a reconfigure CAVStream* stream = FindStreamFromRemoteSEID(aRemoteSEID); if (!stream) { TAvdtpSockAddr addr; addr.SetBTAddr(RemoteAddress()); addr.SetSEID(aRemoteSEID); stream = &aLocalSEP.CreateStreamL(addr, *this, iProtocol); streamCreated = ETrue; CleanupStack::PushL(stream); } else { __ASSERT_DEBUG(stream->RemoteSEID()==aRemoteSEID, Panic(EAvdtpSignallingSessionFoundWrongStream)); } stream->SetConfigurationL(aConfigBuffer, *iSignallingChannel, aReportingConfigured, aRecoveryConfigured); if (streamCreated) { // SetConfigurationL ok, pop stream if we created one CleanupStack::Pop(); } } else { User::Leave(KErrDisconnected); } }void CSignallingSession::CreateLocalSEPL(TAvdtpSEPInfo& aSEPInfo) { LOG_FUNC // create an idle sep into queue for later discovery and selection CLocalSEP* idleSEP = CLocalSEP::NewL(aSEPInfo, iProtocol.SEIDManager()); iLocalSEPs.AddFirst(*idleSEP); // return the SEID to GAVDP via the reference paramter // adjust the service class of CoD, using reference counting of codman iProtocol.CodMan().RegisterCodService(aSEPInfo.IsSink() ? EMajorServiceRendering : EMajorServiceCapturing); }CLocalSEP* CSignallingSession::FindLocalSEP(TSEID aSEID) const { LOG_FUNC __ASSERT_DEBUG(aSEID.IsLocal(), Panic(EAvdtpSEIDHasWrongDomain)); CLocalSEP* s = NULL; TDblQueIter<CLocalSEP> iter(iLocalSEPs); while (iter) { s = iter++; if (s->SEID()==aSEID) { return s; } } return NULL; }CLocalSEP& CSignallingSession::FindLocalSEPL(TSEID aSEID) const { LOG_FUNC CLocalSEP* sep = FindLocalSEP(aSEID); if (!sep) { User::Leave(KErrNotFound); } return *sep; }CAVStream* CSignallingSession::FindStreamFromRemoteSEID(const TSEID& aRemoteSEID) const { LOG_FUNC __ASSERT_DEBUG(!aRemoteSEID.IsLocal(), Panic(EAvdtpSEIDHasWrongDomain)); TDblQueIter<CLocalSEP> iter(iLocalSEPs); while (iter) { CAVStream* s = iter++->Stream(); if (s && s->RemoteSEID()==aRemoteSEID) { return s; } } return NULL; }void CSignallingSession::SignallingChannelError(TInt aError) { LOG_FUNC // set all SEPs to be not in use TDblQueIter<CLocalSEP> iter(iLocalSEPs); while(iter) { CLocalSEP* sep = iter++; sep->SetInUse(EFalse); // need to inform sessions - so tell stream if (sep->Stream()) { DestroyStream(sep->Stream(),aError); } } // leave the channel ClearSignallingChannel(); // tell the user, on all channels iSAP.Error(aError); iSAP.SessionDisconnect(); TPckgBuf<TInternalAvdtpErrorIndication> error; error().iError = aError; iSAP.Indication(error); }// sigsession has two error paths - one by being a channel user, and the other for protocol errorsvoid CSignallingSession::ServiceError(TInt aError) { LOG_FUNC iSAP.Error(aError); }void CSignallingSession::SignallingChannelReady(CSignallingChannel& aNewChannel) { LOG_FUNC // we may have migrated from old signalling channel to the new instance // so cannot use old signalling channel pointer iSignallingChannel = &aNewChannel; SetRemoteAddress(aNewChannel.RemoteAddress()); if (iIsListening) { TInternalAvdtpSignallingReadyIndication ind; ind.iBuf = aNewChannel.RemoteAddress().Des(); TPckg<TInternalAvdtpSignallingReadyIndication> indPckg(ind); iSAP.Indication(indPckg); iIsListening = EFalse; } else { iSAP.Ready(); } }void CSignallingSession::DestroyStream(CAVStream* aStream, TInt aError) { // tell all sessions of the error aStream->NotifyUserPlaneTransportSessionsError(NULL, aError); // and remove the stream delete aStream; }CCapabilityNotAllowedVisitor::CCapabilityNotAllowedVisitor(TAvdtpServiceCategories aLocallySupported): iLocallySupported(aLocallySupported), iIsValid(ETrue) { LOG_FUNC }TBool CCapabilityNotAllowedVisitor::Capability(TAvdtpServiceCategory aCat) { LOG_FUNC // need to put aCat into correct bit position TAvdtpServiceCategories cat; cat.SetCapability(aCat); if ((cat() ^ iLocallySupported()) & cat()) { // remote has wrongly used a cap we don't support, mark it as dud iWrongCategory = aCat; iIsValid = EFalse; } return iIsValid; // will therefore stop at first broken capability }TBool CCapabilityNotAllowedVisitor::IsValid() const { LOG_FUNC return iIsValid; }TAvdtpServiceCategory CCapabilityNotAllowedVisitor::WrongCategory() const { LOG_FUNC return iWrongCategory; }