/*
* Copyright (c) 2002-2005 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: In this state, BTSAC is listening for remote connection
* from a stereo audio accessory
*
*/
// INCLUDE FILES
#include "btsacStateConnected.h"
#include "btsacStateConfigured.h"
#include "btsacStateConfiguring.h"
#include "btsacStateListening.h"
#include "btsacStateAborting.h"
#include "btsacactive.h"
#include "btsacGavdp.h"
#include "btsacSEPManager.h"
#include "btsacStreamerController.h"
#include "debug.h"
// A2DP codec-specific element definitions
// in bluetoothAV.h
using namespace SymbianBluetoothAV;
// Subband codec-specific values
// in bluetoothAV.h
using namespace SymbianSBC;
const TInt KSEPDiscoverResponseDelay = 5000000; // 5 sec
const TInt KGetCapabilitiesResponseDelay = 4000000; // 4 sec
const TInt KSEPConfigureResponseDelay = 4000000; // 4 sec
const TInt KCreateBearersResponseDelay = 5000000; // 5 sec
const TInt KWaitConfStartIndicationDelay = 3000000; // 3 sec
const TInt KOneSecondDelay = 1000000; // 1 sec
// ================= MEMBER FUNCTIONS =======================
// -----------------------------------------------------------------------------
// CBtsacConnected::NewL
// -----------------------------------------------------------------------------
//
CBtsacConnected* CBtsacConnected::NewL(CBTSAController& aParent, TBTConnType aConnType)
{
CBtsacConnected* self = new( ELeave ) CBtsacConnected(aParent, aConnType);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
// -----------------------------------------------------------------------------
// CBtsacConnected::CBtsacConnected
// -----------------------------------------------------------------------------
//
CBtsacConnected::CBtsacConnected(CBTSAController& aParent, TBTConnType aConnType)
: CBtsacState(aParent, EStateConnected), iSuitableSEPFoundAlready(EFalse), iConnType(aConnType),
iSEPFound(ESEPNotInitialized), iRemoteSEPIndex(0), iBearersQuery(EFalse), iCancelConnectReq(EFalse),
iAudioOpenedBy(EAudioOpenedByNone)
{
}
// -----------------------------------------------------------------------------
// CBtsacConnected::ConstructL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::ConstructL()
{
iTimerActive = CBtsacActive::NewL(*this, CActive::EPriorityStandard, KRequestIdTimer);
iTimer.CreateLocal();
}
// -----------------------------------------------------------------------------
// CBtsacConnected::~CBtsacConnected
// -----------------------------------------------------------------------------
//
CBtsacConnected::~CBtsacConnected()
{
TRACE_FUNC_ENTRY
delete iTimerActive;
iTimerActive = NULL;
iTimer.Close();
TRACE_FUNC_EXIT
}
// -----------------------------------------------------------------------------
// CBtsacConnected::EnterL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::EnterL()
{
TRACE_STATE(_L("[BTSAC State] Connected"))
_LIT(KName, "CBtsacStateConnected");
const TDesC& Name = KName;
Parent().iGavdp->RegisterObserver(this, Name);
switch(iConnType)
{
case EOutgoingConnWithAudio:
case EOutgoingConn:
{
if(iConnType == EOutgoingConnWithAudio)
{
iAudioOpenedBy = EAudioOpenedByAFW;
}
StartTimer(KSEPDiscoverResponseDelay, KRequestIdTimer);
Parent().iGavdp->DiscoverSEPs(); // start with SEP discovery
break;
}
case EOutgoingConnWithAudioNoDiscovery:
case EOutgoingConnNoDiscovery:
{
if(iConnType == EOutgoingConnWithAudioNoDiscovery)
{
iAudioOpenedBy = EAudioOpenedByAFW;
}
ConfigureL();
break;
}
case EIncomingConn:
{
Parent().NewAccessory(Parent().GetRemoteAddr());
break;
}
case EConnExists:
break;
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::StartTimer
// -----------------------------------------------------------------------------
//
void CBtsacConnected::StartTimer(TTimeIntervalMicroSeconds32 aTimeout, TInt aRequestId)
{
TRACE_FUNC
if (iTimerActive)
{
if(!iTimerActive->IsActive())
{
iTimerActive->SetRequestId(aRequestId);
iTimer.After(iTimerActive->iStatus, aTimeout);
iTimerActive->GoActive();
}
}
else
{
TRACE_INFO((_L("CBtsacConnected::StartTimer, Timer Active doesn't exist.")))
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::StopTimer
// -----------------------------------------------------------------------------
//
void CBtsacConnected::StopTimer()
{
TRACE_FUNC
if (iTimerActive)
{
iTimerActive->Cancel();
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::CancelActionL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::CancelActionL(TInt aError)
{
TRACE_FUNC
StopTimer();
Parent().CompletePendingRequests((KConnectReq | KOpenAudioReq), aError);
Parent().ChangeStateL(CBtsacListening::NewL(Parent(), EGavdpResetReasonGeneral, aError));
}
// -----------------------------------------------------------------------------
// CBtsacConnected::OpenAudioLinkL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::OpenAudioLinkL(const TBTDevAddr& aAddr)
{
TRACE_FUNC
if (Parent().GetRemoteAddr() != aAddr)
{
TRACE_INFO((_L("CBtsacConnected::OpenAudio, Error!")))
CancelActionL(KErrNotFound);
return;
}
iAudioOpenedBy = EAudioOpenedByAFW;
Parent().SetRemoteAddr(aAddr);
if(iConnType == EIncomingConn)
{
// Accessory has created connection to us. Don't open audio immediately,
// let's wait for a while if accessory starts configuration. This way
// we can avoid configure collision.
StartTimer(KOneSecondDelay, KRequestIdWaitRemoteConfStart);
}
else
{
StartConfigurationL();
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::CancelOpenAudioLinkL()
// -----------------------------------------------------------------------------
//
void CBtsacConnected::CancelOpenAudioLinkL()
{
TRACE_FUNC
CancelActionL(KErrCancel);
}
// -----------------------------------------------------------------------------
// CBtsacConnected::DisconnectL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::DisconnectL()
{
TRACE_FUNC
Parent().CompletePendingRequests(KDisconnectReq, KErrNone);
CancelActionL(KErrCancel);
}
// -----------------------------------------------------------------------------
// CBtsacConnected::CancelConnectL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::CancelConnectL()
{
TRACE_FUNC
if(!iBearersQuery)
{
CancelActionL(KErrCancel);
}
else
{
// We have to wait bearers to be completed
TRACE_INFO((_L("CBtsacConnected::CancelConnectL() Wait for BearerReady.")))
iCancelConnectReq = ETrue;
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::StartConfigurationL()
// -----------------------------------------------------------------------------
//
void CBtsacConnected::StartConfigurationL()
{
TRACE_FUNC
if (!Parent().iRemoteSEPs->NoOfSEPs()) // if accessory info not stored already
{
iRemoteSEPIndex = 0;
Parent().InitializeSEPManager();
StartTimer(KSEPDiscoverResponseDelay, KRequestIdTimer);
Parent().iGavdp->DiscoverSEPs(); // start with SEP discovery
}
else // otherwise we have explored this accessory before - go straight to configure
{
ConfigureL();
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::ConfigureL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::ConfigureL()
{
TRACE_FUNC
RPointerArray<TAvdtpServiceCapability> SEPCapabilities;
if ((Parent().iRemoteSEPs->GetCaps(Parent().GetSEPIndex(), SEPCapabilities)) )
{
TRACE_INFO((_L("CBtsacConnected::Configure() Couldn't retrieve Capabilities !")))
CancelActionL(KErrCancel);
SEPCapabilities.Close();
return;
}
TRACE_INFO((_L("CBtsacConnected::Configure() Retrieve %d capabilities"), SEPCapabilities.Count()))
// loop through all capablities to find sbc codec capablities
TSBCCodecCapabilities SBCCaps;
TAvdtpMediaTransportCapabilities MedTransCaps;
for (TInt index=0; index < SEPCapabilities.Count(); index++)
{
TAvdtpServiceCapability* cap = SEPCapabilities[index];
TAvdtpServiceCategory cat = cap->Category();
if ( cat == EServiceCategoryMediaCodec )
{
SBCCaps = static_cast<TSBCCodecCapabilities&>(*cap);
TRACE_INFO((_L("CBtsacConnected::Configure() SBC Caps retrieved.")))
}
else if ( cat == EServiceCategoryMediaTransport )
{
MedTransCaps = static_cast<TAvdtpMediaTransportCapabilities&>(*cap);
TRACE_INFO((_L("CBtsacConnected::Configure() Media Transport Caps retrieved.")))
}
}
SEPCapabilities.Close();
// Check if headset's capabilities suits us
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Sampling Frequencies: %d"), SBCCaps.SamplingFrequencies()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Channel modes: %d"), SBCCaps.ChannelModes()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Blocks: %d"), SBCCaps.BlockLengths()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory SubBands: %d"), SBCCaps.Subbands()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Alloc method: %d"), SBCCaps.AllocationMethods()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Max bitpool: %d"), SBCCaps.MaxBitpoolValue()))
TRACE_INFO((_L("CBtsacConnected::Configure() Accessory Min bitpool: %d"), SBCCaps.MinBitpoolValue()))
if (Parent().iStreamer->ConfigureSEP(SBCCaps) )
{
TRACE_INFO((_L("CBtsacConnected::Configure() Streamer couldn't configure SEP !")))
CancelActionL(KErrCancel);
return; // capabilites doesn't suit us
}
TAvdtpSEPInfo SEPInfo;
if (Parent().iRemoteSEPs->GetInfo(Parent().GetSEPIndex(), SEPInfo))
{
TRACE_INFO((_L("CBtsacConnected::Configure() Couldn't retrieve SEP Info !")))
CancelActionL(KErrCancel);
return; // capabilites doesn't suit us
}
TSEID remoteSEPid = SEPInfo.SEID();
// local sep index is hard coded cause current implementation is only sbc encoded
TSEID localSEPid = TSEID(1, ETrue);
StartTimer(KSEPConfigureResponseDelay, KRequestIdTimer);
if ( Parent().iGavdp->ConfigureSEP(localSEPid, remoteSEPid , SBCCaps, MedTransCaps ) )
{
TRACE_INFO((_L("CBtsacConnected::Configure() ConfigureSEP returned Error !!!")))
CancelActionL(KErrCancel);
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GetCapabilitiesOfAllSEPs
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GetCapabilitiesOfAllSEPs()
{
TRACE_INFO((_L("CBtsacConnected::GetCapabilitiesOfAllSEPs() CurrentSEP:%d"), iRemoteSEPIndex))
TRACE_INFO((_L("CBtsacConnected::GetCapabilitiesOfAllSEPs() Total Remote SEPs registered:%d"), Parent().iRemoteSEPs->NoOfSEPs()))
if ( iRemoteSEPIndex < Parent().iRemoteSEPs->NoOfSEPs() )
{
TAvdtpSEPInfo SEPInfo;
if (!Parent().iRemoteSEPs->GetInfo(iRemoteSEPIndex, SEPInfo))
{
TSEID id = SEPInfo.SEID();
Parent().iGavdp->GetCapabilities(id);
}
}
else // we have covered all SEPs
{
StopTimer();
iSuitableSEPFoundAlready = EFalse;
if ( iSEPFound == ESEPConfigure )
{
TRAP_IGNORE(ConfigureL());
}
else if ( iSEPFound == ESEPInUse )
{
// We have open audio or connect request pending but do not complete it yet. We expect that
// accessory will start configure us. We should receive GAVDP_ConfigurationStartIndication
// from the remote and remote will configure us. Eventually acc FW's open audio request
// will be completed in CBTsacConfigured state and connect request will be completed in
// CBTsacConfiguring state.
// For safety's sake start timer to protect the situation where we do not receive
// GAVDP_ConfigurationStartIndication.
StartTimer(KWaitConfStartIndicationDelay, KRequestIdTimer);
}
else // no audio sbc sink sep found
{
TRAP_IGNORE(CancelActionL(KErrCancel));
}
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_SEPDiscovered
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_SEPDiscovered(const TAvdtpSEPInfo& aSEPInfo)
{
TRACE_INFO((_L("CBtsacConnected:::GAVDP_SEPDiscovered() SEID: %d InUse: %d MediaType: %d IsSink: %d"),
aSEPInfo.SEID().SEID(), aSEPInfo.InUse(), aSEPInfo.MediaType(), aSEPInfo.IsSink()))
// BTSAC cares only about audio SEPs which are sink
if (aSEPInfo.MediaType() == EAvdtpMediaTypeAudio && aSEPInfo.IsSink() )
{
TRAPD(err, Parent().iRemoteSEPs->NewSEPL(aSEPInfo))
if (!err)
{
Parent().iRemoteSEPs->SetState(aSEPInfo.SEID(), CBTSACStreamEndPoint::EDiscoveredRemote, &aSEPInfo);
}
else // internal problem
{
TRAP_IGNORE(CancelActionL(KErrCancel));
}
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_SEPDiscoveryComplete
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_SEPDiscoveryComplete()
{
TRACE_FUNC
StopTimer();
if ( Parent().iRemoteSEPs->NoOfSEPs() )
{
StartTimer(KGetCapabilitiesResponseDelay, KRequestIdTimer);
GetCapabilitiesOfAllSEPs();
}
else // remote A2DP has no 'audio' 'sink' SEPs ! naughty remote
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_SEPDiscoveryComplete() Remote A2dP has no 'audio' 'sink' SEPs !")))
TRAP_IGNORE(CancelActionL(KErrCancel));
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_SEPCapability
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_SEPCapability(TAvdtpServiceCapability* aCapability)
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_SEPCapability(%d)"), aCapability->Category()))
Parent().iRemoteSEPs->StoreCaps(iRemoteSEPIndex, aCapability);
if( iSuitableSEPFoundAlready )
{
return;
}
if ( aCapability->Category() == EServiceCategoryMediaCodec )
{
TAvdtpMediaCodecCapabilities& codecCaps = static_cast<TAvdtpMediaCodecCapabilities&>(*aCapability);
TAvdtpSEPInfo SEPInfo;
TBool InUse = EFalse;
if ( !Parent().iRemoteSEPs->GetInfo(iRemoteSEPIndex, SEPInfo))
{
InUse = SEPInfo.InUse();
}
else
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_SEPCapability() Couldn't retrieve SEP Info !")))
return;
}
if ( (codecCaps.MediaCodecType() == EAudioCodecSBC) && !InUse) // found SEP that we are interested in
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_SEPCapability() Found SBC Audio Sink SEP !!!")))
iSuitableSEPFoundAlready = ETrue;
iSEPFound = ESEPConfigure;
Parent().SetSEPIndex(iRemoteSEPIndex);
}
else
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_SEPCapability() Remote SEP In Use or Codec Type not suitable !!!")))
iSEPFound = InUse ? ESEPInUse : ESEPCodecTypeNotAllowed;
}
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_SEPCapabilityComplete
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_SEPCapabilityComplete()
{
TRACE_FUNC
iRemoteSEPIndex++;
GetCapabilitiesOfAllSEPs();
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_ConfigurationConfirm
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_ConfigurationConfirm()
{
TRACE_FUNC
StopTimer();
TAvdtpSEPInfo SEPInfo;
if (Parent().iRemoteSEPs->GetInfo(Parent().GetSEPIndex(), SEPInfo))
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_ConfigurationConfirm Couldn't retrieve SEP Info !")))
TRAP_IGNORE(CancelActionL(KErrCancel));
return; // cannot get remote SEP capabilites
}
TSEID remoteSEPid = SEPInfo.SEID();
TRACE_INFO((_L("CBtsacConnected::GAVDP_ConfigurationConfirm() Asking for bearer for remote SEID(%d)"), remoteSEPid.SEID()))
iBearersQuery = ETrue;
StartTimer(KCreateBearersResponseDelay, KRequestIdTimer);
Parent().iGavdp->CreateBearers(remoteSEPid);
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_BearerReady
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_BearerReady(RSocket aNewSocket, const TAvdtpSockAddr& aAddr)
{
TRACE_FUNC
TRACE_INFO((_L("CBtsacConnected::GAVDP_BearerReady() SEID: %d"), aAddr.SEID().SEID()))
(void)aAddr.SEID();
StopTimer();
if(!iCancelConnectReq)
{
if(iConnType != EIncomingConn)
{
// New accessory has been already informed if connection type is incoming,
// otherwise tell about new accessory
Parent().NewAccessory(Parent().GetRemoteAddr());
}
if(iConnType == EOutgoingConn || iConnType == EOutgoingConnNoDiscovery)
{
// If connection is without audio, just complete pending connect request if any.
// Otherwise pending request(s) is/are completed in configured state.
Parent().CompletePendingRequests(KConnectReq, KErrNone);
}
TRAP_IGNORE(Parent().ChangeStateL(CBtsacConfigured::NewL(Parent(), aNewSocket, iAudioOpenedBy, EStreamConfiguredBySrc)));
}
else
{
TRACE_ASSERT(Parent().iStreamingSockets.Count() == 0, EBTPanicSocketExists)
TRACE_INFO((_L("[SOCKET] created.")))
Parent().iStreamingSockets.Append(aNewSocket);
Parent().CompletePendingRequests(KCompleteAllReqs, KErrCancel);
TRAP_IGNORE(Parent().ChangeStateL(CBtsacListening::NewL(Parent(), EGavdpResetReasonGeneral, KErrNone)));
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_AbortIndication
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_AbortIndication(TSEID aSEID)
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_AbortIndication() SEID:%d"), aSEID.SEID()))
(void)aSEID;
TRAP_IGNORE(CancelActionL(KErrDisconnected));
}
// -----------------------------------------------------------------------------
// CBtsacConnected::GAVDP_ConfigurationStartIndication
// -----------------------------------------------------------------------------
//
void CBtsacConnected::GAVDP_ConfigurationStartIndication(TSEID aLocalSEID, TSEID aRemoteSEID)
{
TRACE_INFO((_L("CBtsacConnected::GAVDP_ConfigurationStartIndication() LocalSEID: %d, RemoteSEID: %d"), aLocalSEID.SEID(), aRemoteSEID.SEID()))
StopTimer();
Parent().InitializeSEPManager();
TRAP_IGNORE(Parent().ChangeStateL(CBtsacConfiguring::NewL(Parent(), aLocalSEID, aRemoteSEID)));
}
// -----------------------------------------------------------------------------
// CBtsacConnected::RequestCompletedL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::RequestCompletedL(CBtsacActive& aActive)
{
TRACE_FUNC
switch(aActive.RequestId())
{
case KRequestIdTimer:
{
if(!iBearersQuery)
{
// Go to listening state, gavdp will be shutdown in listening state
CancelActionL(KErrDisconnected);
}
else
{
// If bearers query timer has expired, lets handle it separately.
Parent().iGavdp->Cancel();
TInt err = Parent().AbortStream();
if(!err)
{
Parent().ChangeStateL(CBtsacAborting::NewL(Parent()));
}
else
{
CancelActionL(KErrDisconnected);
}
}
break;
}
case KRequestIdWaitRemoteConfStart:
{
// Remote didn't configure us and we have open audio request from acc fw.
// Let's configure the link and open audio by our selves.
StartConfigurationL();
break;
}
default:
{
TRACE_INFO((_L("CBtsacConnected::RequestCompletedL() Unknown request")))
break;
}
}
}
// -----------------------------------------------------------------------------
// CBtsacConnected::CancelRequest
// -----------------------------------------------------------------------------
//
void CBtsacConnected::CancelRequest(CBtsacActive& aActive)
{
TRACE_FUNC
(void)aActive;
iTimer.Cancel();
}
// -----------------------------------------------------------------------------
// CBtsacConnected::HandleGavdpErrorL
// -----------------------------------------------------------------------------
//
void CBtsacConnected::HandleGavdpErrorL(TInt aError)
{
TRACE_FUNC
StopTimer();
switch (aError)
{
case KErrAvdtpRequestTimeout: // -18005
{
TRACE_INFO((_L("CBtsacConnected::HandleGavdpErrorL() Request TIMEOUT")))
// Go to listening state, gavdp will be shutdown in listening state
CancelActionL(KErrDisconnected);
break;
}
case KErrHCILinkDisconnection: // -6305
case KErrDisconnected: // -36
{
TRACE_INFO((_L("CBtsacConnected::HandleGavdpErrorL() Signalling disconnected.")))
// for both outgoing or incoming connection, if we have an error,
// this means there is disconnection
CancelActionL(aError);
break;
}
case (KErrAvdtpSignallingErrorBase - EAvdtpSEPInUse): // -18064
{
// Remote SEP is in use.
// Wait for a while, accessory might configure us. If we don't receive
// start ind during KOneSecondDelay, go to listening state.
StartTimer(KOneSecondDelay, KRequestIdTimer);
break;
}
default:
// KErrNotReady -18
// KErrInUse -14
// KErrCorrupt -20
// (KErrAvdtpSignallingErrorBase - EAvdtpBadState) -18094
{
CancelActionL(KErrDisconnected);
}
}
}
// End of File