bluetooth/btstack/avdtp/avdtpStream.cpp
changeset 0 29b1cd4cb562
child 22 786b94c6f0a4
--- /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();
+	}
+