diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avdtp/avdtpSignallingSession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avdtp/avdtpSignallingSession.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1837 @@ +// 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 +#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 _DEBUG +PANICCATEGORY("avdtpsigse"); +#endif + +CSignallingSession* 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 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 SAP +TInt 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(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(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(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(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(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(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 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(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(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(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(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(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*/) +#else +void 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(reinterpret_cast(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(reinterpret_cast(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(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(aOption.Ptr()); + // client doesn't manage inUse field - we do + info.SetInUse(EFalse); + + TRAP(ret, const_cast(this)->CreateLocalSEPL(info)); + + TPckg 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 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 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; iCapabilities().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 not +if the remote has then we reject the request +else 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 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 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; iConfiguration().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 forbids +see 8.10.1 avdtp sepc +if the remote has then we reject the request +else 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 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 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 by +the 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 channels; + channels[0] = static_cast(sep->Stream()->iMediaBinding.iChannel); + channels[1] = static_cast(sep->Stream()->iReportingBinding.iChannel); + channels[2] = static_cast(sep->Stream()->iRecoveryBinding.iChannel); + TArray 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 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 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 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 indPckg(ind); + iSAP.Indication(indPckg); + + return KErrNone; + } + } + +void CSignallingSession::SecurityControlConfirm(TInt aResult, TSEID aRemoteSEID, const TDesC8& aResponseData) + { + LOG_FUNC + if (aResult==KErrNone) + { + TPckgBuf 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 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 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 indPckg(ind); + iSAP.Indication(indPckg); + } + +void CSignallingSession::StreamConfigured(TSEID /*aLocalSEID*/, TInt aResult, TAvdtpServiceCategory aFailedCategory) + { + LOG_FUNC + // tell the user in all cases + TPckgBuf 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 machine +The signalling session SHALL NOT mutate the stream based on this; the stream should +proceed, 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 indication +to 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 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 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 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 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 error; + error().iError = aError; + iSAP.Indication(error); + } + +// sigsession has two error paths - one by being a channel user, and the other for protocol errors +void 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 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; + } +