--- /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 <bluetooth/logger.h>
+#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();
+ }
+