diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avdtp/avdtpStreamStates.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avdtp/avdtpStreamStates.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1422 @@ +// 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 statemachine +// +// + +/** + @file + @internalComponent +*/ + +#include +#include "avdtpStream.h" +#include "avdtp.h" +#include "avdtpSignallingChannel.h" +#include "avdtpSignallingSession.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_AVDTP); +#endif + +#ifdef _DEBUG +#define DEBUGPANICINSTATE(aPanic) PanicInState(aPanic) +#else +#define DEBUGPANICINSTATE(aPanic) +#endif + +// state ctors + +TAVStreamState::TAVStreamState(CAVStreamStateFactory& aFactory) +: iFactory(aFactory) + { + STATENAME("BASE"); + } + +TAVStreamStateIdle::TAVStreamStateIdle(CAVStreamStateFactory& aFactory) +: TAVStreamState(aFactory) + { + STATENAME("Idle"); + } + +TAVStreamStateOpen::TAVStreamStateOpen(CAVStreamStateFactory& aFactory) +: TAVStreamStateIdle(aFactory) + { + STATENAME("Open"); + } + +TAVStreamStateReady::TAVStreamStateReady(CAVStreamStateFactory& aFactory) +: TAVStreamStateOpen(aFactory) + { + STATENAME("Ready"); + } + +TAVStreamStateWaitForSessions::TAVStreamStateWaitForSessions(CAVStreamStateFactory& aFactory) +: TAVStreamStateIdle(aFactory) + { + STATENAME("WaitForSessions"); + } + +TAVStreamStateWaitForSessionsStartReceived::TAVStreamStateWaitForSessionsStartReceived(CAVStreamStateFactory& aFactory) +: TAVStreamStateWaitForSessions(aFactory) + { + STATENAME("WaitForSessionsStartReceived"); + } + +TAVStreamStateConfiguring::TAVStreamStateConfiguring(CAVStreamStateFactory& aFactory) +: TAVStreamStateIdle(aFactory) + { + STATENAME("Configuring"); + } + +TAVStreamStateConfigured::TAVStreamStateConfigured(CAVStreamStateFactory& aFactory) +: TAVStreamStateIdle(aFactory) + { + STATENAME("**Abstract** Configure"); + } + +TAVStreamStateINTConfigured::TAVStreamStateINTConfigured(CAVStreamStateFactory& aFactory) +: TAVStreamStateConfigured(aFactory) + { + STATENAME("INTConfigured"); + } + +TAVStreamStateACPConfigured::TAVStreamStateACPConfigured(CAVStreamStateFactory& aFactory) +: TAVStreamStateConfigured(aFactory) + { + STATENAME("ACPConfigured"); + } + +TAVStreamStateOpening::TAVStreamStateOpening(CAVStreamStateFactory& aFactory) +: TAVStreamStateIdle(aFactory) + { + STATENAME("Opening"); + } + +TAVStreamStateCreatingLogicalChannels::TAVStreamStateCreatingLogicalChannels(CAVStreamStateFactory& aFactory) +: TAVStreamStateOpen(aFactory) + { + STATENAME("CreatingLogicalChannels"); + } + +TAVStreamStateReleasing::TAVStreamStateReleasing(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Releasing"); + } + +TAVStreamStateAborting::TAVStreamStateAborting(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Aborting"); + } + +TAVStreamStateStreaming::TAVStreamStateStreaming(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Streaming"); + } + +TAVStreamStateStarting::TAVStreamStateStarting(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Starting"); + } + +TAVStreamStateSuspending::TAVStreamStateSuspending(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Suspending"); + } + +TAVStreamStateWaitingForLogicalChannels::TAVStreamStateWaitingForLogicalChannels(CAVStreamStateFactory& aFactory) +: TAVStreamStateOpen(aFactory) + { + STATENAME("WaitingForLogicalChannels"); + } + +TAVStreamStateReconfiguring::TAVStreamStateReconfiguring(CAVStreamStateFactory& aFactory) +: TAVStreamStateReady(aFactory) + { + STATENAME("Reconfiguring"); + } + + +// default state actions + +void TAVStreamState::Enter(CAVStream& /*aStream*/) const + { + LOG_FUNC + // do nothing by default + } + +void TAVStreamState::Exit(CAVStream& /*aStream*/) const + { + LOG_FUNC + // do nothing by default + } + +void TAVStreamState::Release(CAVStream& aStream) const + { + LOG_FUNC + // it already has been Released + aStream.Released(); + } + +void TAVStreamState::AwaitLogicalChannelsL(CAVStream& /*aStream*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedAwaitLogicalChannelsEvent); + } + +void TAVStreamState::LogicalChannelsAvailable(CAVStream& /*aStream*/, TLogicalChannelFactoryTicket /*aTicket*/, TInt /*aError*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedLogicalChannelsAvailableEvent); + } + +void TAVStreamState::WatchdogFired(CAVStream& /*aStream*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedWatchdogFiredEvent); + } + +void TAVStreamState::Configured(CAVStream& /*aStream*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedConfiguredEvent); + } + +void TAVStreamState::SetConfigurationL(CAVStream& /*aStream*/, + RBuf8& /*aPacketBuffer*/, CSignallingChannel& /*aSignallingChannel*/, + TBool /*aReportingConfigured*/, + TBool /*aRecoveryConfigured*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedSetConfigurationEvent); + User::Leave(KErrNotReady); + } + +void TAVStreamState::SetConfigConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, + TSEID /*aRemoteSEID*/, TAvdtpServiceCategory /*aFailedCategory*/) const + { + // throw away + } + +void TAVStreamState::ReconfigConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, + TSEID /*aRemoteSEID*/, TAvdtpServiceCategory /*aFailedCategory*/) const + { + LOG_FUNC + // throw away + } + +void TAVStreamState::StartConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // throw away + } + +void TAVStreamState::AbortConfirm(CAVStream& /*aStream*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // throw away + } + +void TAVStreamState::OpenConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // default, throw away weirdo response from remote + } + +void TAVStreamState::ReleaseConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // throw away + } + +void TAVStreamState::SuspendConfirm(CAVStream& /*aStream*/, TInt /*aResult*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // throw away + } + +TInt TAVStreamState::AddSession(CAVStream& /*aStream*/, + TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/, + CTransportChannel*& /*aTransportChannel*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedAddSessionEvent); + return KErrNotReady; + } + +void TAVStreamState::DropSession(CAVStream& /*aStream*/, TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + DEBUGPANICINSTATE(EAvdtpUnexpectedDropSession); + } + +TInt TAVStreamState::StartReceived(CAVStream& /*aStream*/) const + { + LOG_FUNC + return KErrNotReady; + } + +TBool TAVStreamState::IsStreaming(const CAVStream& /*aStream*/) const + { + LOG_FUNC + return EFalse; + } + +TInt TAVStreamState::Start(CAVStream& /*aStream*/) const + { + LOG_FUNC + return KErrNotReady; + } + +TInt TAVStreamState::Suspend(CAVStream& /*aStream*/) const + { + LOG_FUNC + return KErrNotReady; + } + +void TAVStreamState::Started(CAVStream& /*aStream*/) const + { + LOG_FUNC + // signalling session should have sanity checked with previous StartReceived() + DEBUGPANICINSTATE(EAvdtpUnexpectedStartedEvent); + } + +void TAVStreamState::Suspended(CAVStream& /*aStream*/) const + { + LOG_FUNC + // signalling session should have sanity checked with previous IsStreaming + DEBUGPANICINSTATE(EAvdtpUnexpectedSuspendedEvent); + } + +void TAVStreamState::ChangeState(CAVStream& aContext, CAVStreamStateFactory::TAVStreamStates aState) const + { + LOG_FUNC + aContext.iState->Exit(aContext); + +#ifdef __FLOG_ACTIVE + const TAVStreamState* const newState=&iFactory.GetState(aState); + LOG2(_L("Stream: State %S -> %S"), &aContext.iState->iName, &newState->iName); +#endif //__FLOG_ACTIVE + + aContext.iState=&iFactory.GetState(aState); + aContext.iState->Enter(aContext); + } + +void TAVStreamState::PanicInState(TAvdtpPanic aPanic) const + { + Panic(aPanic, iFactory.StateIndex(this)); + } + + +// derived states and state changes + +void TAVStreamStateIdle::Configured(CAVStream& aStream) const + { + // ah - going ACP + ChangeState(aStream, CAVStreamStateFactory::EStreamStateACPConfigured); + } + +void TAVStreamStateIdle::SetConfigurationL(CAVStream& aStream, + RBuf8& aPacketBuffer, + CSignallingChannel& aSignallingChannel, + TBool aReportingConfigured, + TBool aRecoveryConfigured) const + { + LOG_FUNC +// An INT procedure to add in extra options the stack wishes to do, regardless of client +// At present this is limited to mux&frag - which we do if local and remote have +// the *capability* to do so, even if the proposed *configuration* doesnt mention it + +// AVDTP is daft, so have to arrange for IDs before objects exist if muxing used + +// we also need to base this on the number of sessions we know we're going to run +// see 8.19.8 +// 5.4.6 allow fors no reporting session so it's not *obvious* when the 2nd element is +// bit like not obvious what the 2nd L2cap channel will bear + +// further, if we're muxing, we need to see what optional sessions are proposed +// in the configuration to see what to configure in the mux capability + + TBool canMux; + TBool canFrag; + aStream.CanMuxFrag(canMux, canFrag); + + TInt err = KErrNone; + + if (canMux) + { + // muxing is awkward - must identify the TCs and TSs that are going to be used NOW + TAvdtpMultiplexingCapability* mux = aStream.CreateMuxCapabilityL(aReportingConfigured, aRecoveryConfigured, aSignallingChannel); + + // ok to create packetstructure - only exchange IDs if do mux + err = mux->AsProtocol(aPacketBuffer); + } + + if (!err) + { + // First check that we are in a state to send a set configuration command as + // we don't want to send one unless we can commit the pending configuration + err = aStream.iLocalSEP->CheckConfigurationState(EFalse); + + if (!err) + { + err = aSignallingChannel.SendSetConfiguration(aStream, + aStream.LocalSEID(), aStream.RemoteSEID(), aPacketBuffer); + if (!err) + { + // tell SEP + err = aStream.iLocalSEP->SetPendingConfiguration(aPacketBuffer, EFalse); //takes ownership of RBuf + + // we should not get an error here as we called CheckConfigurationState above + __ASSERT_DEBUG(!err, PanicInState(EAvdtpInconsistentPendingConfigurationState)); + + if (!err) + { + aPacketBuffer.Assign(NULL); // since ownership transfer succeeded + ChangeState(aStream, CAVStreamStateFactory::EStreamStateConfiguring); + } + } + } + } + + if (err) + { + // clean up and don't bother proceeding + aStream.DeallocateUnclaimedTransportSessionIDs(); + User::Leave(err); + } + } + +void TAVStreamStateIdle::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + + +void TAVStreamStateConfiguring::SetConfigConfirm(CAVStream& aStream, TInt aResult, + TSEID /*aRemoteSEID*/, TAvdtpServiceCategory aFailedCategory) const + { + LOG_FUNC + //update our state before telling others + TRAPD(res, aStream.iLocalSEP->CommitPendingConfigurationL(aResult==KErrNone, EFalse)); + + if (res!=KErrNone) + { + aResult = res; + } + + if (aResult==KErrNone) + { + TBool rp = aStream.CheckConfigured(EServiceCategoryReporting); + TBool rc = aStream.CheckConfigured(EServiceCategoryRecovery); + aStream.iNumSessionsRequired = AvdtpInternalUtils::NumberOfTransportObjects(rp, rc); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateINTConfigured); + } + + aStream.iStreamNotify.StreamConfigured(aStream.LocalSEID(), aResult, aFailedCategory); + + if (aResult!=KErrNone) + { + // remove the stream as the configure failed the stream is invalid + aStream.Released(); // deletes stream + } + } + +void TAVStreamStateConfiguring::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateConfigured::Release(CAVStream& aStream) const + { + // in this state to Release a stream object (cos it errored say) + // needs us to send an Abort rather than a Close to remote + // this is because the remote will need to mark its SEP as not in use, + // but we haven't yet sent an Open. + LOG_FUNC + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.iRemoteAddress.BTAddr()); + + if (sigch) + { + TInt err = sigch->SendAbort(aStream, aStream.RemoteSEID()); + if (err==KErrNone) + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateAborting); + } + else + { + // just have to die + aStream.Released(); + } + } + } + +void TAVStreamStateConfigured::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +/** +An INT primitive +A session is adding itself to the stream. +Record its details and find a channel for it to use. +Record the binding between session and channel. +Once all the required sessions are added the stream tries to open the remote +*/ +TInt TAVStreamStateINTConfigured::AddSession(CAVStream& aStream, + TAvdtpTransportSessionType aType, + CUserPlaneTransportSession& aSession, + CTransportChannel*& aChannel) const + { + LOG_FUNC + TInt ret = KErrNone; + // check GAVDP isn't being naughty and asking for an unconfigured session + if ((aType==EReporting && !aStream.CheckConfigured(EServiceCategoryReporting)) + || (aType==ERecovery && !aStream.CheckConfigured(EServiceCategoryRecovery))) + { + // gavdp is trying to use a session it has never configured + ret = KErrNotSupported; + } + + if (ret == KErrNone) + { + TBool useMux; + TBool useFrag; //ignored + aStream.CanMuxFrag(useMux, useFrag); + + aChannel = aStream.iProtocol.GetTransportChannel(aStream.iRemoteAddress, useMux); + + if (aChannel) + { + // bind session to channel + ret = aChannel->AttachTransportSession(aSession, aType); + if (ret==KErrNone) + { + // keep a copy of this binding + TTransportBinding binding; + binding.iSession = &aSession; + binding.iChannel = aChannel; + + // keep object bindings + TTransportBinding* b = NULL; + + switch (aType) + { + case EMedia: + b = &aStream.iMediaBinding; + break; + case EReporting: + b = &aStream.iReportingBinding; + break; + case ERecovery: + b = &aStream.iRecoveryBinding; + break; + } + + __ASSERT_DEBUG(b, PanicInState(EAvdtpBadSessionAttachingToStream)); + // remember binding + *b = binding; + } + TryToOpen(aStream); + } + else + { + ret = KErrNotReady; // TC went down... + } + } + return ret; + } + +/** +One of the sessions in the stream is dropping from the stream. +At this point the stream hadn't been opened, so don't Release stream +Just cancel oustanding logical channel factory request +*/ +void TAVStreamStateINTConfigured::DropSession(CAVStream& aStream, TAvdtpTransportSessionType /*aType*/, CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + aStream.iProtocol.LogicalChannelFactory().Cancel(aStream.iFactoryJob); + } + +void TAVStreamStateINTConfigured::TryToOpen(CAVStream& aStream) const + { + LOG_FUNC + // check how many sessions we think we need + // if all have added to us then we find signalling ch and issue an open + + TInt numSessionsBound = (aStream.iMediaBinding.iSession ?1:0) + + (aStream.iReportingBinding.iSession ?1:0) + + (aStream.iRecoveryBinding.iSession ?1:0); + + + __ASSERT_DEBUG(numSessionsBound>=1 && numSessionsBound<=3, PanicInState(EAVDTPBadSessionCount)); + if (numSessionsBound == aStream.iNumSessionsRequired) + { + // GO! + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.DeviceAddress()); + TInt err = sigch ? sigch->SendOpenStream(aStream, aStream.RemoteSEID()) : KErrNotReady; + if (err==KErrNone) + { + // all sessions joined - so Open + ChangeState(aStream, CAVStreamStateFactory::EStreamStateOpening); + } + else + { + aStream.NotifyUserPlaneTransportSessionsError(NULL, err); + // no need to release stream - leave gavdp client to decide + } + } + } + +void TAVStreamStateINTConfigured::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateINTConfigured::SetConfigurationL(CAVStream& /*aStream*/, + RBuf8& /*aPacketBuffer*/, + CSignallingChannel& /*aSignallingChannel*/, + TBool /*aReportingConfigured*/, + TBool /*aRecoveryConfigured*/) const + { + // GAVDP spec v1.1 sect4.1.5 forbids this - must be at OPEN state before reconfiguring + // in our implementation this means we need to be in Ready state (thus have transport channels) + // yes it is weird... + User::Leave(KErrAvdtpInvalidStateForReconfigure); + } + +/** +ACP configured, now need to await the Open +*/ + +void TAVStreamStateACPConfigured::AwaitLogicalChannelsL(CAVStream& aStream) const + { + LOG_FUNC + const TBool requireReporting = aStream.CheckConfigured(EServiceCategoryReporting); + const TBool requireRecovery = aStream.CheckConfigured(EServiceCategoryRecovery); + + TAvdtpMultiplexingCapability* const muxCap = static_cast + (aStream.iLocalSEP->Configuration()[EServiceCategoryMultiplexing]); + + if (muxCap) + { + TAvdtpMultiplexingCapabilityHelper helper(*muxCap, requireReporting, requireRecovery); + DoAwaitMuxedChannelsL(aStream, helper, requireReporting, requireRecovery); + } + else + { + DoAwaitDirectChannelsL(aStream, requireReporting, requireRecovery); + } + } + + +void TAVStreamStateACPConfigured::DoAwaitMuxedChannelsL(CAVStream& aStream, + TAvdtpMultiplexingCapabilityHelper& aHelper, + TBool aRequireReporting, + TBool aRequireRecovery) const + { + LOG_FUNC + TInt numExtraLogicalChannelsNeeded=0; + TAvdtpSockAddr address = aStream.iRemoteAddress; + // get TCIDs for the sessions in the mux record + // this will indicate the TCIDs that the INT has proposed + + // we look for mux channels with any TCIDs proposed + // and reduce thus reduce the number of TCs we expect + CTransportChannel** tcPtr = &aStream.iMediaBinding.iChannel; + + *tcPtr = aStream.iProtocol.FindMuxChannel(aHelper.MediaCID()); + if (!*tcPtr) + { + address.SetSession(EMedia); + *tcPtr = aStream.iProtocol.GetTransportChannel(address, ETrue, aHelper.MediaCID()); + User::LeaveIfNull(*tcPtr); + numExtraLogicalChannelsNeeded++; + } + + tcPtr = &aStream.iReportingBinding.iChannel; + if (aRequireReporting) + { + __ASSERT_DEBUG(aHelper.ReportingCID() != KInvalidTCID, PanicInState(EAVDTPBadMuxConfiguration)); + *tcPtr = aStream.iProtocol.FindMuxChannel(aHelper.ReportingCID()); + if (!*tcPtr) + { + // might need a new channel + if (aHelper.ReportingCID()!=aHelper.MediaCID()) + { + address.SetSession(EReporting); + *tcPtr = aStream.iProtocol.GetTransportChannel(address, ETrue, aHelper.ReportingCID()); + User::LeaveIfNull(*tcPtr); + numExtraLogicalChannelsNeeded++; + } + // else remote has proposed putting reporting in same channel as media + } + } + + tcPtr = &aStream.iRecoveryBinding.iChannel; + if (aRequireRecovery) + { + __ASSERT_DEBUG(aHelper.RecoveryCID() != KInvalidTCID, PanicInState(EAVDTPBadMuxConfiguration)); + *tcPtr = aStream.iProtocol.FindMuxChannel(aHelper.RecoveryCID()); + if (!*tcPtr) + { + // might need a new channel + if (aHelper.RecoveryCID()!=aHelper.MediaCID() && aHelper.RecoveryCID()!=aHelper.ReportingCID()) + { + address.SetSession(ERecovery); + *tcPtr = aStream.iProtocol.GetTransportChannel(address, ETrue, aHelper.RecoveryCID()); + User::LeaveIfNull(*tcPtr); + numExtraLogicalChannelsNeeded++; + } + else if (aHelper.RecoveryCID()==aHelper.MediaCID()) + { + __DEBUGGER(); + User::Leave(KErrCorrupt); // remote has contravened spec + } + } + } + + if (!numExtraLogicalChannelsNeeded) + { + // everything we need is already here, kick state machine and wait for sessions to attach + ChangeState(aStream, CAVStreamStateFactory::EStreamStateWaitForSessions); + // tell signalling session + aStream.iStreamNotify.StreamAccepted(aStream.LocalSEID(), + aStream.RemoteSEID(), aRequireReporting, aRequireRecovery); + } + else + { + // TCs were created above, now wait for appropriate logical channels + aStream.iFactoryJob = aStream.iProtocol.LogicalChannelFactory(). + ExpectSessionLogicalChannelsL(aStream, + numExtraLogicalChannelsNeeded); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateWaitingForLogicalChannels); + } + } + + +void TAVStreamStateACPConfigured::DoAwaitDirectChannelsL(CAVStream& aStream, + TBool aRequireReporting, + TBool aRequireRecovery) const + { + LOG_FUNC + // create the necessary Transport Channels ready for the logical channels + // assume all inbound TCs are mux -our seps always do muxing, and allows for "upgrades" + // if others SEPs want to add sessions to them later + + TInt numExtraLogicalChannelsNeeded = 0; + TAvdtpSockAddr address = aStream.iRemoteAddress; + address.SetSession(EMedia); + +#ifdef __SYMBIAN_AVDTP_HIDE_MUX + #define CAN_BECOME_MUX EFalse +#else +#pragma message("Not sure if this true - remember morgan, cannot upgrade as dont know which "direct" channel has which TCID") + #define CAN_BECOME_MUX ETrue +#endif + + aStream.iMediaBinding.iChannel = aStream.iProtocol.GetTransportChannel(address, CAN_BECOME_MUX); + User::LeaveIfNull(aStream.iMediaBinding.iChannel); + numExtraLogicalChannelsNeeded++; + + if (aRequireReporting) + { + address.SetSession(EReporting); + aStream.iReportingBinding.iChannel = aStream.iProtocol.GetTransportChannel(address, CAN_BECOME_MUX); + User::LeaveIfNull(aStream.iReportingBinding.iChannel); + numExtraLogicalChannelsNeeded++; + } + + if (aRequireRecovery) + { + address.SetSession(ERecovery); + aStream.iRecoveryBinding.iChannel = aStream.iProtocol.GetTransportChannel(address, CAN_BECOME_MUX); + User::LeaveIfNull(aStream.iRecoveryBinding.iChannel); + numExtraLogicalChannelsNeeded++; + } + + LOG1(_L8("Awaiting %d logical channels"), numExtraLogicalChannelsNeeded); + + // wait for appropriate logical channels + aStream.iFactoryJob = aStream.iProtocol.LogicalChannelFactory(). + ExpectSessionLogicalChannelsL(aStream, + numExtraLogicalChannelsNeeded); + + ChangeState(aStream, CAVStreamStateFactory::EStreamStateWaitingForLogicalChannels); + } + +TInt TAVStreamStateACPConfigured::AddSession(CAVStream& /*aStream*/, + TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/, + CTransportChannel*& /*aTransportChannel*/) const + { + LOG_FUNC + return KErrNotReady; + } + +void TAVStreamStateACPConfigured::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + + +void TAVStreamStateOpen::DropSession(CAVStream& aStream, TAvdtpTransportSessionType /*aType*/, CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + // if any session in the stream is shutdown + // we invalidate the stream, and thus send a Release + + // we leave the GC to actually shutdown the session saps + DoRelease(aStream); + // leave unbinding transport objects until ReleaseConfirm + } + +void TAVStreamStateOpen::Release(CAVStream& aStream) const + { + LOG_FUNC + // we need to Release this as the object is going + DoRelease(aStream); + } + +void TAVStreamStateOpen::DoRelease(CAVStream& aStream) const + { + LOG_FUNC + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.iRemoteAddress.BTAddr()); + + if (sigch) + { + sigch->SendRelease(aStream, aStream.RemoteSEID()); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReleasing); + } + } + +void TAVStreamStateOpen::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +TInt TAVStreamStateReady::AddSession(CAVStream& /*aStream*/, + TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/, + CTransportChannel*& /*aTransportChannel*/) const +{ +LOG_FUNC +#ifdef _DEBUG +return KErrPanicAvdtpOpenInBadState; +#else +return KErrNotReady; +#endif +} + +void TAVStreamStateReady::SetConfigurationL(CAVStream& aStream, + RBuf8& aPacketBuffer, + CSignallingChannel& aSignallingChannel, + TBool __DEBUG_ONLY(aReportingConfigured), + TBool __DEBUG_ONLY(aRecoveryConfigured)) const + { + // this is a Reconfigure - see GAVDP spec + LOG_FUNC + __ASSERT_DEBUG(!aReportingConfigured && !aRecoveryConfigured, PanicInState(EAvdtpReconfigurationCapabilitiesNotChecked)); + + // First check that we are in a state to send a reconfigure command as + // we don't want to send one unless we can commit the pending configuration + TInt err = aStream.iLocalSEP->CheckConfigurationState(ETrue); + + if (!err) + { + err = aSignallingChannel.SendReconfigure(aStream, aStream.RemoteSEID(), aPacketBuffer); + + if (!err) + { + // tell SEP + err = aStream.iLocalSEP->SetPendingConfiguration(aPacketBuffer, ETrue); //takes ownership of RBuf + + // we should not get an error here as we called CheckConfigurationState above + __ASSERT_DEBUG(!err, PanicInState(EAvdtpInconsistentPendingReconfigurationState)); + + if (!err) + { + aPacketBuffer.Assign(NULL); // since ownership transfer succeeded + // release configuration buffer as we own it in this state + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReconfiguring); + } + } + } + + User::LeaveIfError(err); + } + +/** + * Before the OpenStream returns, gavdp might get Cancel()ed. At this point we have an outstanding + * request on the signalling channel. We can't issue a cancel on the individual request, and we + * can't close sigch down as it's shared, but there should only be one outstanding for this stream, + * so we can cancel all transactions for it. + */ +void TAVStreamStateOpening::DropSession(CAVStream& aStream, TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.DeviceAddress()); + __ASSERT_DEBUG(sigch, PanicInState(EAvdtpSignallingChannelShouldExist)); + sigch->CancelTransactions(aStream); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateIdle); + } + +void TAVStreamStateOpening::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateOpening::OpenConfirm(CAVStream& aStream, TInt aResult, TSEID __DEBUG_ONLY(aRemoteSEID)) const + { + LOG_FUNC + // good! + __ASSERT_DEBUG(aRemoteSEID == aStream.RemoteSEID(), PanicInState(EAvdtpConfirmAddressedToWrongRequester)); + TInt ret = KErrNone; + +// now we kick TCs into life + if (aResult) + { + // can return to Idle, user could try this operation again + // the way Open works means we have transport sessions interested in result of this + // they need to tell their sockets that their connects failed + + ChangeState(aStream, CAVStreamStateFactory::EStreamStateIdle); + + // no need to tell signalling session as Opening is not available to RGavdp + // Opening is performed when the necessary sockets are created and connected in a stream + aStream.NotifyUserPlaneTransportSessionsError(NULL, aResult); + } + else + { + // create a job - see what logical channels need creating + TInt numLogicalChannelsRequired = + aStream.iMediaBinding.iChannel->IsConnected() ? 0 : 1; + + if (aStream.iReportingBinding.iChannel) + { + // a reporting transport channel is required - but is it available? + if (!aStream.iReportingBinding.iChannel->IsConnected()) + { + // this is required, so add into job + numLogicalChannelsRequired++; + } + } + + if (aStream.iRecoveryBinding.iChannel) + { + // a recovery transport channel is required - but is it available? + if (!aStream.iRecoveryBinding.iChannel->IsConnected()) + { + // this is required, so add into job + numLogicalChannelsRequired++; + } + } + + if (numLogicalChannelsRequired) + { + // if either required, kick off request... + ChangeState(aStream, CAVStreamStateFactory::EStreamStateCreatingLogicalChannels); + TLogicalChannelFactoryTicket ticket; + TRAP(ret, ticket=aStream.iProtocol.LogicalChannelFactory().CreateSessionLogicalChannelsL( + aStream.iRemoteAddress.BTAddr(), + aStream, numLogicalChannelsRequired)); + if (ret==KErrNone) + { + aStream.iFactoryJob = ticket; // record this, so we can cancel if needed + } + else + { + // stay in same state + ChangeState(aStream, CAVStreamStateFactory::EStreamStateOpening); + } + } + else + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateOpen); + // all TCs appear to be available without creating new ones + aStream.TransportChannelsReady(); + } + } + } + + +TInt TAVStreamStateReady::StartReceived(CAVStream& aStream) const + { + LOG_FUNC + + aStream.ReadyForStartIndication(); + + return KErrNone; + } + +TInt TAVStreamStateReady::Start(CAVStream& aStream) const + { + LOG_FUNC + TInt retVal = KErrNone; + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.iRemoteAddress.BTAddr()); + + // should always have sigch here - hence function signature + __ASSERT_DEBUG(sigch, PanicInState(EAvdtpSignallingChannelShouldExist)); + if (sigch) + { + retVal = sigch->SendStartStream(aStream, aStream.RemoteSEID()); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateStarting); + } + return retVal; + } + +void TAVStreamStateReady::Started(CAVStream& aStream) const + { + LOG_FUNC + ChangeState(aStream, CAVStreamStateFactory::EStreamStateStreaming); + } + +void TAVStreamStateReady::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateStarting::Enter(CAVStream& aStream) const + { + LOG_FUNC + // Tell the physical link that encryption keys could be refreshed now, to prevent an auto refresh later + aStream.TryToAndThenPreventHostEncryptionKeyRefresh(); + } + + +void TAVStreamStateStarting::StartConfirm(CAVStream& aStream, TInt aResult, TSEID /*aRemoteSEID*/) const + { + LOG_FUNC + // just change state + if (aResult==KErrNone) + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateStreaming); + aStream.iStreamNotify.StreamStarted(aStream.LocalSEID()); + } + else + { + // whether suspended or idle, we return to ready + // but first release the prevent encryption key refresh token + aStream.AllowHostEncryptionKeyRefresh(); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, aResult); + } + } + +TInt TAVStreamStateStarting::StartReceived(CAVStream& /*aStream*/) const + { + LOG_FUNC + + // we need to tell the other end 'nope we're busy' because the starts 'crossed in the post' + // returning KErrInUse will tell the other end to go away (politely of course) + return KErrInUse; + } + + +void TAVStreamStateReleasing::Release(CAVStream& /*aStream*/) const + { + LOG_FUNC + // we are already releasing so do nothing. + } + +void TAVStreamStateReleasing::ReleaseConfirm(CAVStream& aStream, TInt /*aResult*/, TSEID /*aSEID*/) const + { + LOG_FUNC + // ignore error - proceed anyway + aStream.Released(); + } + +void TAVStreamStateReleasing::DropSession(CAVStream& /*aStream*/, TAvdtpTransportSessionType /*aType*/, CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + // to guard against sending multiple Releases per dropped session + // since we're releasing, we don't need to send a Release, but we just consume + } + +void TAVStreamStateReleasing::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + + +void TAVStreamStateAborting::AbortConfirm(CAVStream& aStream, TSEID /*aSEID*/) const + { + LOG_FUNC + aStream.Released(); + } + +void TAVStreamStateAborting::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + + +void TAVStreamStateAborting::Release(CAVStream& /*aStream*/) const + { + LOG_FUNC + // we are already releasing so do nothing. + } + + +void TAVStreamStateCreatingLogicalChannels::LogicalChannelsAvailable(CAVStream& aStream, TLogicalChannelFactoryTicket aTicket, TInt aError) const + { + LOG_FUNC + aStream.iFactoryJob = aTicket; + aStream.BindLogicalAndTransportChannels(aTicket, aError); + + if (aError==KErrNone) + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + // tell sessions... + aStream.TransportChannelsReady(); + } + else + { + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, aError); + } + } + + +void TAVStreamStateCreatingLogicalChannels::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateWaitingForLogicalChannels::Enter(CAVStream& aStream) const + { + LOG_FUNC + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + + // start guard timer in case logical channels never arrive for TCs + aStream.StartWatchdog(); + } + +void TAVStreamStateWaitingForLogicalChannels::LogicalChannelsAvailable(CAVStream& aStream, TLogicalChannelFactoryTicket aResponse, TInt aError) const + { + LOG_FUNC + // we created the transport channels in :Await, so now we can bind + // then the sessions can call :AddSession and scoop up the TCs + aStream.iFactoryJob = aResponse; + aStream.BindLogicalAndTransportChannels(aResponse, aError); + + if (aError==KErrNone) + { + const TBool reportingPresent = aStream.iReportingBinding.iChannel ? ETrue : EFalse; + const TBool recoveryPresent = aStream.iRecoveryBinding.iChannel ? ETrue : EFalse; + + aStream.iNumSessionsRequired = AvdtpInternalUtils::NumberOfTransportObjects(reportingPresent, recoveryPresent); + ChangeState(aStream, CAVStreamStateFactory::EStreamStateWaitForSessions); + // tell signalling session + aStream.iStreamNotify.StreamAccepted(aStream.LocalSEID(), + aStream.RemoteSEID(), reportingPresent, recoveryPresent); + } + else + { + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, aError); + } + } + +void TAVStreamStateWaitingForLogicalChannels::DropSession(CAVStream& aStream, TAvdtpTransportSessionType /*aType*/, + CUserPlaneTransportSession& /*aSession*/) const + { + LOG_FUNC + aStream.iProtocol.LogicalChannelFactory().Cancel(aStream.iFactoryJob); + } + +void TAVStreamStateWaitingForLogicalChannels::Exit(CAVStream& aStream) const + { + LOG_FUNC + // start guard timer in case logical channels never arrive for TCs + aStream.StopWatchdog(); + } + +void TAVStreamStateWaitingForLogicalChannels::WatchdogFired(CAVStream& aStream) const + { + LOG_FUNC + // need to tidy TCs, and Abort stream and cancel job + TLogicalChannelFactoryTicket nullTicket; + + aStream.BindLogicalAndTransportChannels(nullTicket, KErrTimedOut); + + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, KErrTimedOut); + } + + +/** +An ACP primitive: the TCs & LCs are ready, now we are awaiting the sessions +to come in and connect to the TCs. The sessions do so at the behest of GAVDP +*/ +TInt TAVStreamStateWaitForSessions::AddSession(CAVStream& aStream, + TAvdtpTransportSessionType aType, + CUserPlaneTransportSession& aSession, + CTransportChannel*& aChannel) const + { + LOG_FUNC + TInt ret = KErrNone; + + // bind session to channel - the channel is there, we'd have been told if it had gone + TTransportBinding* b = NULL; + + switch (aType) + { + case EMedia: + b = &aStream.iMediaBinding; + break; + case EReporting: + // check GAVDP isn't being naughty and asking for an unconfigured session + if (aStream.CheckConfigured(EServiceCategoryReporting)) + { + b = &aStream.iReportingBinding; + } + else + { + // GAVDP asking for a bearer that is not configured + ret = KErrNotSupported; + } + break; + case ERecovery: + // check GAVDP isn't being naughty and asking for an unconfigured session + if (aStream.CheckConfigured(EServiceCategoryRecovery)) + { + b = &aStream.iRecoveryBinding; + } + else + { + // GAVDP asking for a bearer that is not configured + ret = KErrNotSupported; + } + break; + default: + PanicInState(EAvdtpBadSessionAttachingToStream); + } + + if (ret == KErrNone) + { + ret = b->iChannel->AttachTransportSession(aSession, aType); + if (ret==KErrNone) + { + // stream needs to remember binding + b->iSession = &aSession; + // and session need to know channel + aChannel = b->iChannel; + // check if all sessions have bound + if (!--aStream.iNumSessionsRequired) + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + aStream.TransportChannelsReady(); + } + } + } + return ret; + } + +TInt TAVStreamStateWaitForSessions::StartReceived(CAVStream& aStream) const + { + LOG_FUNC + ChangeState(aStream, CAVStreamStateFactory::EStreamStateWaitForSessionsStartReceived); + return KErrNone; + } + +void TAVStreamStateWaitForSessions::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +/** +Overrides the method in TAVStreamStateWaitForSessions. The start has already been received by +CSignallingSession::StartIndication, so after changing the state to EStreamStateReady data +stored in the CAVStream object will be used to pass a start indication to GAVDP. +*/ +TInt TAVStreamStateWaitForSessionsStartReceived::AddSession(CAVStream& aStream, + TAvdtpTransportSessionType aType, + CUserPlaneTransportSession& aSession, + CTransportChannel*& aChannel) const + { + LOG_FUNC + // Call the function from the parent class to bind the session + TInt ret = TAVStreamStateWaitForSessions::AddSession(aStream,aType,aSession,aChannel); + + // if that worked, and all the channels are bound, we can now issue the delayed start + if((ret == KErrNone) && (!aStream.iNumSessionsRequired)) + { + aStream.ReadyForStartIndication(); + } + + return ret; + } + +TInt TAVStreamStateWaitForSessionsStartReceived::StartReceived(CAVStream& /*aStream*/) const + { + LOG_FUNC + return KErrNotReady; + } + +void TAVStreamStateWaitForSessionsStartReceived::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateSuspending::SuspendConfirm(CAVStream& aStream, TInt aResult, TSEID /*aRemoteSEID*/) const + { + LOG_FUNC + // just change state + if (aResult==KErrNone) + { + // no difference between a "suspended" state, and ready + // making them the same helps support reconfig in more states + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + aStream.iStreamNotify.StreamSuspended(aStream.LocalSEID()); + } + else + { + // stay in streaming state and notify the user of the error + ChangeState(aStream, CAVStreamStateFactory::EStreamStateStreaming); + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, aResult); + } + } + +void TAVStreamStateSuspending::Suspended(CAVStream& /*aStream*/) const + { + LOG_FUNC + // throw away + } + + +void TAVStreamStateSuspending::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + +void TAVStreamStateReconfiguring::ReconfigConfirm(CAVStream& aStream, TInt aResult, + TSEID /*aRemoteSEID*/, TAvdtpServiceCategory aFailedCategory) const + { + LOG_FUNC + // ok to return to ready on success or failure + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + TRAPD(err, aStream.iLocalSEP->CommitPendingConfigurationL(aResult==KErrNone, ETrue)); + if (err!=KErrNone) + { + aStream.iStreamNotify.StreamInitiatedServiceFailed(aStream, aResult); + } + aStream.iStreamNotify.StreamConfigured(aStream.LocalSEID(), aResult, aFailedCategory); + } + +void TAVStreamStateReconfiguring::Enter(CAVStream& __DEBUG_ONLY(aStream)) const + { + LOG_FUNC + //We should not be preventing the encryption key refresh in this state. Asserting this condition. + __ASSERT_DEBUG(!aStream.IsHostEncryptionKeyRefreshPrevented(),Panic(EUnexpectedEncryptionKeyRefreshPrevent)); + } + + +void TAVStreamStateStreaming::Enter(CAVStream& aStream) const + { + LOG_FUNC + // experiments with window dragging show not much change if thread priority bumped + // maybe due to trying to get into esock in the first place + // Changing the role of the Esock_BT thread in the cmi file, from player to + // DealerPlayer allows messages to be passed directly to the Esock_BT thread + + aStream.iOriginalESockBTPriority = RThread().Priority(); //save the original priority of the Esock_BT thread. + RThread().SetPriority(EPriorityRealTime); + + } + +void TAVStreamStateStreaming::Exit(CAVStream& aStream) const + { + LOG_FUNC + RThread().SetPriority(aStream.iOriginalESockBTPriority);//set the priority of the thread back to the original value. + + // Tell the physical link that encryption keys could be refreshed now, to prevent an auto refresh later + aStream.AllowHostEncryptionKeyRefresh(); + } + +TInt TAVStreamStateStreaming::Suspend(CAVStream& aStream) const + { + LOG_FUNC + TInt retVal = KErrNone; + CSignallingChannel* sigch = aStream.iProtocol.FindSignallingChannel(aStream.iRemoteAddress.BTAddr()); + + __ASSERT_DEBUG(sigch, PanicInState(EAvdtpSignallingChannelShouldExist)); + if (sigch) + { + retVal = sigch->SendSuspendStream(aStream, aStream.RemoteSEID()); + if (retVal==KErrNone) + { + ChangeState(aStream, CAVStreamStateFactory::EStreamStateSuspending); + } + } + return retVal; + } + +void TAVStreamStateStreaming::Suspended(CAVStream& aStream) const + { + LOG_FUNC + ChangeState(aStream, CAVStreamStateFactory::EStreamStateReady); + } + +TBool TAVStreamStateStreaming::IsStreaming(const CAVStream& /*aStream*/) const + { + LOG_FUNC + return ETrue; + } + +TInt TAVStreamStateStreaming::Start(CAVStream& /*aStream*/) const + { + // don't want to tell remote to Start when already Streaming, cos it's BadState + return KErrNotReady; + } + +TInt TAVStreamStateStreaming::StartReceived(CAVStream& /*aStream*/) const + { + LOG_FUNC + return KErrNotReady; + } +