diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avdtp/avdtpStream.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avdtp/avdtpStream.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,660 @@ +// 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 stream +// +// + +/** + @file + @internalComponent +*/ + +#include +#include "avdtpStream.h" +#include "avdtpsap.h" +#include "avdtp.h" +#include "avdtputil.h" +#include "avdtpSignallingChannel.h" +#include "avdtpSignallingSession.h" +#include "avdtpLocalSEP.h" + +#include "avdtputil.h" +#include "btconsts.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_AVDTP); +#endif + +CAVStreamStateFactory* CAVStreamStateFactory::NewL() + { + LOG_STATIC_FUNC + CAVStreamStateFactory* ret=new (ELeave) CAVStreamStateFactory(); + CleanupStack::PushL(ret); + ret->ConstructL(); + CleanupStack::Pop(); + return ret; + } + +void CAVStreamStateFactory::ConstructL() + { + LOG_FUNC + iStates[EStreamStateIdle]=new (ELeave) TAVStreamStateIdle(*this); + iStates[EStreamStateWaitForSessions]=new (ELeave) TAVStreamStateWaitForSessions(*this); + iStates[EStreamStateWaitingForLogicalChannels]=new (ELeave) TAVStreamStateWaitingForLogicalChannels(*this); + iStates[EStreamStateCreatingLogicalChannels]=new (ELeave) TAVStreamStateCreatingLogicalChannels(*this); + iStates[EStreamStateConfiguring]=new (ELeave) TAVStreamStateConfiguring(*this); + iStates[EStreamStateINTConfigured]=new (ELeave) TAVStreamStateINTConfigured(*this); + iStates[EStreamStateACPConfigured]=new (ELeave) TAVStreamStateACPConfigured(*this); + iStates[EStreamStateOpening]=new (ELeave) TAVStreamStateOpening(*this); + iStates[EStreamStateOpen]=new (ELeave) TAVStreamStateOpen(*this); + iStates[EStreamStateReady]=new (ELeave) TAVStreamStateReady(*this); + iStates[EStreamStateStarting]=new (ELeave) TAVStreamStateStarting(*this); + iStates[EStreamStateSuspending]=new (ELeave) TAVStreamStateSuspending(*this); + iStates[EStreamStateReconfiguring]=new (ELeave) TAVStreamStateReconfiguring(*this); + iStates[EStreamStateReleasing]=new (ELeave) TAVStreamStateReleasing(*this); + iStates[EStreamStateAborting]=new (ELeave) TAVStreamStateAborting(*this); + iStates[EStreamStateStreaming]=new (ELeave) TAVStreamStateStreaming(*this); + iStates[EStreamStateWaitForSessionsStartReceived]=new (ELeave) TAVStreamStateWaitForSessionsStartReceived(*this); + } + +CAVStreamStateFactory::~CAVStreamStateFactory() + { + LOG_FUNC + iStates.DeleteAll(); + } + +const TAVStreamState& CAVStreamStateFactory::GetState(CAVStreamStateFactory::TAVStreamStates aState) const + { + LOG_FUNC + __ASSERT_DEBUG(aState != EStreamMaxState, Panic(EAvdtpMuxerStateOutOfBounds)); + return *iStates[aState]; + } + +TInt CAVStreamStateFactory::StateIndex(const TAVStreamState* aState) const + { + TInt state; + for (state = 0; state < EStreamMaxState; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + + return KAvdtpUnknownState; + } + + +CAVStream* CAVStream::NewL(const TAvdtpSockAddr& aAddr, + MAvdtpStreamNotify& aStreamNotify, + CAvdtpProtocol& aProtocol, + CLocalSEP& aLocalSEP) + { + LOG_STATIC_FUNC + CAVStream* stream = new (ELeave) CAVStream(aAddr, + aStreamNotify, + aProtocol, + aLocalSEP); + CleanupStack::PushL(stream); + stream->ConstructL(); + CleanupStack::Pop(); + return stream; + } + +void CAVStream::ConstructL() + { + LOG_FUNC + iWatchdogTimer = CWatchdogTimer::NewL(*this); + // watchdog actually starts Just-in-time + } + +CAVStream::CAVStream(const TAvdtpSockAddr& aAddr, + MAvdtpStreamNotify& aStreamNotify, + CAvdtpProtocol& aProtocol, + CLocalSEP& aLocalSEP) +: iProtocol(aProtocol), iStreamNotify(aStreamNotify), iLocalSEP(&aLocalSEP), iNotifiedACLStreamActive(EFalse) + { + LOG_FUNC + iRemoteAddress = aAddr; + __ASSERT_DEBUG(!iRemoteAddress.SEID().IsLocal(), Panic(EAvdtpSEIDHasWrongDomain)); + __ASSERT_DEBUG(iLocalSEP->SEID().IsLocal(), Panic(EAvdtpSEIDHasWrongDomain)); + + iState = &iProtocol.StreamStateFactory().GetState(CAVStreamStateFactory::EStreamStateIdle); + } + +CAVStream::~CAVStream() + { + LOG_FUNC + + // We might already have done this, but call again regardless, as + // the dtor might be called whilst still in the streaming state + AllowHostEncryptionKeyRefresh(); + + // cancel outstanding operations on signalling channel + CSignallingChannel* sigCh = iProtocol.FindSignallingChannel(iRemoteAddress.BTAddr()); + + if (sigCh) + { + sigCh->CancelTransactions(*this); + } + + iProtocol.LogicalChannelFactory().Cancel(iFactoryJob); + + if (iLocalSEP) + { + iLocalSEP->StreamDestroyed(*this); + } + // remove ourselves from protocol list + iProtocol.RemoveStream(*this); + + // tidyup up any pre-allocated transport session IDs + DeallocateUnclaimedTransportSessionIDs(); + + delete iWatchdogTimer; + } + +void CAVStream::DeallocateUnclaimedTransportSessionIDs() + { + LOG_FUNC + for (TInt i=0; i<=iMuxedTSIDs.Count()-1; i++) + { + iMuxedTSIDs[i].Close(); + } + } + +#ifdef _DEBUG +void CAVStream::LocalSEPDestroyed(const CLocalSEP& aTerminator) +#else +void CAVStream::LocalSEPDestroyed(const CLocalSEP& /*aTerminator*/) +#endif + { + LOG_FUNC + __ASSERT_DEBUG(&aTerminator == iLocalSEP, Panic(EAvdtpBadLocalSEPInstructingStreamRelease)); + // release the stream if needed + iLocalSEP = NULL; + Release(); + } + +void CAVStream::StartWatchdog() + { + LOG_FUNC + iWatchdogTimer->After(KAvdtpLogicalChannelsWatchdogTimeout); + } + +void CAVStream::StopWatchdog() + { + LOG_FUNC + iWatchdogTimer->Cancel(); + } + +void CAVStream::WatchdogFired() + { + LOG_FUNC + iState->WatchdogFired(*this); + } + +void CAVStream::LogicalChannelFactoryRequestComplete(TLogicalChannelFactoryTicket aTicket, TInt aError) + { + LOG_FUNC + iState->LogicalChannelsAvailable(*this, aTicket, aError); + } + + +void CAVStream::OpenConfirm(TInt aResult, TSEID aRemoteSEID) + { + LOG_FUNC + iState->OpenConfirm(*this, aResult, aRemoteSEID); + } + +void CAVStream::ReleaseConfirm(TInt aResult, TSEID aRemoteSEID) + { + LOG_FUNC + iState->ReleaseConfirm(*this, aResult, aRemoteSEID); + } + +void CAVStream::AbortConfirm(TSEID aRemoteSEID) + { + LOG_FUNC + iState->AbortConfirm(*this, aRemoteSEID); + } + +void CAVStream::Configured() + { + LOG_FUNC + iState->Configured(*this); + } + +void CAVStream::AwaitLogicalChannelsL() + { + LOG_FUNC + iState->AwaitLogicalChannelsL(*this); + } + + +TBool CAVStream::IsStreaming() const + { + LOG_FUNC + return iState->IsStreaming(*this); + } + +void CAVStream::Started() + { + LOG_FUNC + iState->Started(*this); + } + +void CAVStream::Suspended() + { + LOG_FUNC + iState->Suspended(*this); + } + +TBool CAVStream::MultiplexingConfigured() const + { + return iLocalSEP->MultiplexingConfigured(); + } + +/** +Call from somewhere that an error needs to be forwarded to the transport sessions +A session can tell us to tell others, but not tell itself +*/ +void CAVStream::NotifyUserPlaneTransportSessionsError(CUserPlaneTransportSession* aErroredSession, TInt aError) + { + LOG_FUNC + if (iMediaBinding.iSession && iMediaBinding.iSession != aErroredSession) + { + // although all streams must have media sessions, the session may not have bound yet (as when we are ACP) + iMediaBinding.iSession->StreamError(aError); + /* this stream error will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + + if (iReportingBinding.iSession && iReportingBinding.iSession != aErroredSession) + { + iReportingBinding.iSession->StreamError(aError); + /* this stream error will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + if (iRecoveryBinding.iSession && iRecoveryBinding.iSession != aErroredSession) + { + iRecoveryBinding.iSession->StreamError(aError); + /* this stream error will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + } + +void CAVStream::Released() + { + LOG_FUNC + + if (iMediaBinding.iSession) + { + // the media binding would have detached now if it was actively shutdown thereby + // leading to this release + iMediaBinding.iSession->StreamReleased(); + /* this stream release will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + + if (iReportingBinding.iSession) + { + iReportingBinding.iSession->StreamReleased(); + /* this stream release will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + if (iRecoveryBinding.iSession) + { + iRecoveryBinding.iSession->StreamReleased(); + /* this stream release will eventually go through to the SAP, and then the + transportsession will be deleted, this in turn causes a call through to + the (public) ClearSession function that will NULL iMediaBinding.iSession + so we don't use it again + */ + } + + delete this; + } + +TInt CAVStream::AddSession(TAvdtpTransportSessionType aType, + CUserPlaneTransportSession& aSession, + CTransportChannel*& aChannel) + { + LOG_FUNC + return iState->AddSession(*this, aType, aSession, aChannel); + } + +void CAVStream::DropSession(TAvdtpTransportSessionType aType, CUserPlaneTransportSession& aSession) + { + LOG_FUNC + iState->DropSession(*this, aType, aSession); + } + + +/** +Called from states as common helper +*/ +void CAVStream::TransportChannelsReady() + { + LOG_FUNC + // notify the sessions - all the TCs are ready. phew. + TBTDevAddr addr = iRemoteAddress.BTAddr(); + + iMediaBinding.iSession->Ready(addr); + + if (iReportingBinding.iSession) + { + iReportingBinding.iSession->Ready(addr); + } + if (iRecoveryBinding.iSession) + { + iRecoveryBinding.iSession->Ready(addr); + } + } + +/* this public vsn is used by transport session and avdtpSAP + when deleting the session we're bound to + */ +void CAVStream::ClearSession(CUserPlaneTransportSession& aSession) + { + LOG_FUNC + if (iMediaBinding.iSession == &aSession) + { + iMediaBinding.iSession = NULL; + } + else if (iReportingBinding.iSession == &aSession) + { + iReportingBinding.iSession = NULL; + } + else if (iRecoveryBinding.iSession == &aSession) + { + iRecoveryBinding.iSession = NULL; + } + } + +TAvdtpMultiplexingCapability* CAVStream::CreateMuxCapabilityL(TBool aRequireReporting, + TBool aRequireRecovery, + CSignallingChannel& aIDProvider) + { + LOG_FUNC + // this is called for when we're muxing + // try to get transport channel objects now (they won't ask for logical channels yet) + // for transport sessions we have to wait as they MUST be associated with a SAP + // which is created when user opens an RSocket... + // so here we just satisfy the weird muxing requirements by allocating the TSID + // then the session can acquire ownership of that later + + // the address used to find a suitable transport channel + TAvdtpMultiplexingCapability* mux = new(ELeave) TAvdtpMultiplexingCapability; + CleanupStack::PushL(mux); + + TAvdtpMultiplexingCapabilityHelper helper(*mux, aRequireReporting, aRequireRecovery); + + TAvdtpSockAddr addr(iRemoteAddress); + addr.SetSession(EMedia); + + iMediaBinding.iChannel = iProtocol.GetTransportChannel(addr, ETrue); + User::LeaveIfNull(iMediaBinding.iChannel); + helper.SetMediaCID(iMediaBinding.iChannel->TCID()); + User::LeaveIfError(aIDProvider.TSIDManager().GetTSID(iMuxedTSIDs[EMedia])); + helper.SetMediaSID(iMuxedTSIDs[EMedia].TSID()); + + if (aRequireReporting) + { + User::LeaveIfError(aIDProvider.TSIDManager().GetTSID(iMuxedTSIDs[EReporting])); + + addr.SetSession(EReporting); + iReportingBinding.iChannel = iProtocol.GetTransportChannel(addr, ETrue); + User::LeaveIfNull(iReportingBinding.iChannel); + helper.SetReportingCID(iReportingBinding.iChannel->TCID()); + helper.SetReportingSID(iMuxedTSIDs[EReporting].TSID()); + } + + if (aRequireRecovery) + { + User::LeaveIfError(aIDProvider.TSIDManager().GetTSID(iMuxedTSIDs[ERecovery])); + + addr.SetSession(ERecovery); + iRecoveryBinding.iChannel = iProtocol.GetTransportChannel(addr, ETrue); + User::LeaveIfNull(iRecoveryBinding.iChannel); + helper.SetRecoveryCID(iRecoveryBinding.iChannel->TCID()); + helper.SetRecoverySID(iMuxedTSIDs[ERecovery].TSID()); + } + + CleanupStack::Pop(mux); + return mux; + } + +void CAVStream::GetTSID(RTSID& aTSID, TAvdtpTransportSessionType aSessionType) + { + LOG_FUNC + aTSID.Acquire(iMuxedTSIDs[aSessionType]); + } + +void CAVStream::Release() + { + LOG_FUNC + iState->Release(*this); + } + +void CAVStream::SetConfigurationL(RBuf8& aPacketBuffer, + CSignallingChannel& aSignallingChannel, + TBool aReportingConfigured, + TBool aRecoveryConfigured) + { + LOG_FUNC + iState->SetConfigurationL(*this, aPacketBuffer, aSignallingChannel, + aReportingConfigured, aRecoveryConfigured); + } + +void CAVStream::SetConfigConfirm(TInt aResult, TSEID aRemoteSEID, TAvdtpServiceCategory aFailedCategory) + { + LOG_FUNC + iState->SetConfigConfirm(*this, aResult, aRemoteSEID, aFailedCategory); + } + +void CAVStream::ReconfigConfirm(TInt aResult, TSEID aRemoteSEID, TAvdtpServiceCategory aFailedCategory) + { + LOG_FUNC + iState->ReconfigConfirm(*this, aResult, aRemoteSEID, aFailedCategory); + } + +void CAVStream::StartConfirm(TInt aResult, TSEID aRemoteSEID) + { + LOG_FUNC + iState->StartConfirm(*this, aResult, aRemoteSEID); + } + +void CAVStream::SuspendConfirm(TInt aResult, TSEID aRemoteSEID) + { + LOG_FUNC + iState->SuspendConfirm(*this, aResult, aRemoteSEID); + } + +TInt CAVStream::Start() + { + LOG_FUNC + return iState->Start(*this); + } + +TInt CAVStream::Suspend() + { + LOG_FUNC + return iState->Suspend(*this); + } + +void CAVStream::TryToAndThenPreventHostEncryptionKeyRefresh() + { + LOG_FUNC + if (iTryToAndThenPreventHostEncryptionKeyRefreshToken) + { + LOG(_L("Already notified physical link of stream starting")); + } + else + { + iProtocol.ControlPlane().TryToAndThenPreventHostEncryptionKeyRefresh(DeviceAddress(), iTryToAndThenPreventHostEncryptionKeyRefreshToken); + } + } + +void CAVStream::AllowHostEncryptionKeyRefresh() + { + LOG_FUNC + MBluetoothControlPlaneToken::Release(iTryToAndThenPreventHostEncryptionKeyRefreshToken); + } + +void CAVStream::CanMuxFrag(TBool& aCanMux, TBool& aCanFrag) const + { + LOG_FUNC + // we know our SEPs have this capability...so just check remote + aCanFrag = EFalse; +#ifdef __SYMBIAN_AVDTP_HIDE_MUX + aCanMux = EFalse; +#else + aCanMux = iProtocol.RemoteSEPCache().HasCapability(iRemoteAddress.BTAddr(), RemoteSEID(), EServiceCategoryMultiplexing); +#endif + } + +TBool CAVStream::CheckConfigured(TAvdtpServiceCategory aCategory) const + { + LOG_FUNC + return iLocalSEP->Configuration()[aCategory] ? ETrue : EFalse; + } + +void CAVStream::BindLogicalAndTransportChannels(TLogicalChannelFactoryTicket aTicket, TInt aError) + { + LOG_FUNC + // a record into which to claim the resulting bearers, one at a time + TLogicalChannelRecord rec; + + // check we're muxing + TBool mux = iLocalSEP->Configuration()[EServiceCategoryMultiplexing]!=NULL; + + TLogicalChannelFactoryTicketInspector inspector(aTicket, !!iReportingBinding.iChannel, !!iRecoveryBinding.iChannel, mux); + + if (aError==KErrNone) + { + rec = inspector.GetLogicalChannel(EMedia); + } + + // bind the possible logical channel into the transport channel for media + iMediaBinding.iChannel->LogicalChannelComplete(rec, aError); + rec.Reset(); + + if (iReportingBinding.iChannel) + { + if (aError==KErrNone) + { + rec = inspector.GetLogicalChannel(EReporting); + } + iReportingBinding.iChannel->LogicalChannelComplete(rec, aError); + } + + rec.Reset(); + + if (iRecoveryBinding.iChannel) + { + if (aError==KErrNone) + { + rec = inspector.GetLogicalChannel(ERecovery); + } + iRecoveryBinding.iChannel->LogicalChannelComplete(rec, aError); + } + } + +/** +A start indication has been received from the remove. +*/ +TInt CAVStream::StartIndication(TAvdtpTransactionLabel aLabel, TSEID aSeid) + { + // Take a copy of the current start info. This means that if the + // state machine synchronously calls back it will find the correct + // details, but if it doesn't accept this start indication we + // can to restore the old info. + TStartDetails oldDetails = iStartDetails; + + iStartDetails.iLabel = aLabel; + iStartDetails.iSeid = aSeid; + + TInt ret = iState->StartReceived(*this); + + if(ret != KErrNone) + { + // restore old start info + iStartDetails = oldDetails; + } + + return ret; + } + +TBool CAVStream::IsHostEncryptionKeyRefreshPrevented() + { + if(iTryToAndThenPreventHostEncryptionKeyRefreshToken) + return ETrue; + else + return EFalse; + } + +/** This is a delayed start indication issued from the state machine. +We now need to pass this on the sig session. +*/ +void CAVStream::ReadyForStartIndication() + { + LOG_FUNC + + iStreamNotify.StreamReadyForStartIndication(iStartDetails.iLabel, iStartDetails.iSeid); + } + +CWatchdogTimer* CWatchdogTimer::NewL(CAVStream& aObserver) + { + LOG_STATIC_FUNC + CWatchdogTimer* dog = new(ELeave) CWatchdogTimer(aObserver); + CleanupStack::PushL(dog); + dog->ConstructL(); + CleanupStack::Pop(); + return dog; + } + +void CWatchdogTimer::ConstructL() + { + LOG_FUNC + CTimer::ConstructL(); + } + +CWatchdogTimer::CWatchdogTimer(CAVStream& aObserver) +: CTimer(EPriorityStandard), iObserver(aObserver) + { + LOG_FUNC + CActiveScheduler::Add(this); + } + +void CWatchdogTimer::RunL() + { + LOG_FUNC + iObserver.WatchdogFired(); + } +