/*
* Copyright (c) 2005-2006 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: Implementation of Connected state.
* Version : %version: 21 %
*
*/
// INCLUDE FILES
#include <e32property.h>
#include <ctsydomainpskeys.h>
#include "basrvaccstateattached.h"
#include "basrvaccstateattach.h"
#include "basrvaccstatedetach.h"
#include "basrvaccstatedisconnect.h"
#include "BTAccInfo.h"
#include "debug.h"
const TInt KRequestIdOpenMonoAudio = 40;
const TInt KRequestIdOpenStereoAudio = 41;
const TInt KRequestIdCloseMonoAudio = 42;
const TInt KRequestIdCloseStereoAudio = 43;
const TInt KRequestIdTimer = 44;
const TInt KRequestIdMonoActiveModeRequest = 45;
const TInt KRequestIdStereoActiveModeRequest = 46;
const TInt KRequestIdConnectRemConTG = 47;
const TInt KAudioCloseResponseDelay = 1200000; // 1.2 sec
// ================= MEMBER FUNCTIONS =======================
CBasrvAccStateAttached* CBasrvAccStateAttached::NewL(CBasrvAcc& aParent, TBool aShowNote)
{
CBasrvAccStateAttached* self=new(ELeave) CBasrvAccStateAttached(aParent, aShowNote);
return self;
}
CBasrvAccStateAttached::~CBasrvAccStateAttached()
{
delete iRemConTGConnector;
delete iAudioOpener;
delete iAudioCloser;
delete iTimerActive;
iTimer.Close();
TRACE_FUNC
}
void CBasrvAccStateAttached::EnterL()
{
StatePrint(_L("Attached"));
iTimer.CreateLocal();
if (AccInfo().iAudioOpenedProfiles)
{
if (AccInfo().iAudioOpenedProfiles & EStereo)
HandleAccOpenedAudio(EStereo);
if (AccInfo().iAudioOpenedProfiles & EHFP)
HandleAccOpenedAudio(EHFP);
if (AccInfo().iAudioOpenedProfiles & EHSP)
HandleAccOpenedAudio(EHSP);
}
else
{
Parent().RequestSniffMode();
}
if (iShowNote)
{
Parent().AccMan().NotifyClientNewProfile(AccInfo().iConnProfiles, AccInfo().iAddr);
TInt callState;
TInt err = RProperty::Get(KPSUidCtsyCallInformation, KCTsyCallState, callState);
if(!err && (callState == EPSCTsyCallStateNone || callState == EPSCTsyCallStateUninitialized))
{
Parent().AccMan().ShowNote(EBTConnected, AccInfo().iAddr);
}
}
}
CBasrvAccState* CBasrvAccStateAttached::ErrorOnEntry(TInt /*aReason*/)
{
TRACE_FUNC
CBasrvAccState* next = NULL;
TRAP_IGNORE(next = CBasrvAccStateDetach::NewL(Parent()));
return next;
}
TBTEngConnectionStatus CBasrvAccStateAttached::ConnectionStatus() const
{
return EBTEngConnected;
}
void CBasrvAccStateAttached::ConnectL(const TBTDevAddr& aAddr)
{
if (aAddr == AccInfo().iAddr)
{
Parent().AccMan().ConnectCompletedL(AccInfo().iAddr, KErrNone, AccInfo().iConnProfiles);
}
else
{
CBasrvAccState::ConnectL(aAddr);
}
}
void CBasrvAccStateAttached::DisconnectL()
{
TRACE_FUNC
Parent().AccMan().RemoveAudioRequest(AccInfo().iAddr);
if (AccInfo().iAudioOpenedProfiles)
{
TAccAudioType type = (AccInfo().iAudioOpenedProfiles & EAnyMonoAudioProfiles)
? EAccMonoAudio : EAccStereoAudio;
Parent().AccMan().AccfwConnectionL()->NotifyAudioLinkCloseL(AccInfo().iAddr, type);
AccInfo().iAudioOpenedProfiles = EUnknownProfile;
}
Parent().ChangeStateL(CBasrvAccStateDetach::NewL(Parent()));
}
void CBasrvAccStateAttached::AccessoryConnected(TProfiles aProfile)
{
TRACE_FUNC
CBasrvAccState::AccessoryConnected(aProfile);
Parent().NotifyLinkChange2Rvc();
Parent().AccMan().NotifyClientNewProfile(aProfile, AccInfo().iAddr);
}
void CBasrvAccStateAttached::AccOpenedAudio(TProfiles aProfile)
{
TRACE_FUNC
delete iTimerActive;
iTimerActive = NULL;
HandleAccOpenedAudio(aProfile);
}
void CBasrvAccStateAttached::AccClosedAudio(TProfiles aProfile)
{
TRACE_FUNC
UpdateAudioState(EAudioLinkClosed, (aProfile == EStereo) ? EAccStereoAudio : EAccMonoAudio);
CBasrvAccState::AccClosedAudio(aProfile);
Parent().NotifyLinkChange2Rvc();
iCloseAudioProfile = aProfile;
if (!iTimerActive)
{
iTimerActive = CBasrvActive::New(*this, CActive::EPriorityStandard, KRequestIdTimer);
}
if (iTimerActive)
{
iTimerActive->Cancel();
iTimer.After(iTimerActive->iStatus, KAudioCloseResponseDelay);
iTimerActive->GoActive();
}
Parent().RequestSniffMode();
StatePrint(_L("Attached"));
}
void CBasrvAccStateAttached::AccessoryDisconnectedL(TProfiles aProfile)
{
TRACE_FUNC
delete iTimerActive;
iTimerActive = NULL;
TInt audiolinkcache = AccInfo().iAudioOpenedProfiles;
if ((AccInfo().iAudioOpenedProfiles & EStereo) && (aProfile & EStereo))
{
Parent().RequestSniffMode();
}
ProfileDisconnected(aProfile);
CBasrvAccState::AccClosedAudio(aProfile);
Parent().NotifyLinkChange2Rvc();
Parent().AccMan().NotifyClientNoProfile(aProfile, AccInfo().iAddr);
StatePrint(_L("Attached"));
// AudioPolicy wants to get a notification about audio link closes before
// detaches as well so here we go
if (audiolinkcache & aProfile ||
iCloseAudioProfile & aProfile )
{
TAccAudioType type = (aProfile & EAnyMonoAudioProfiles)
? EAccMonoAudio : EAccStereoAudio;
Parent().AccMan().AccfwConnectionL()->NotifyAudioLinkCloseL(AccInfo().iAddr, type);
// let's clear this variable here to be sure it's not used twice accidentally
iCloseAudioProfile = EUnknownProfile;
}
if (!(AccInfo().iConnProfiles & EAnyAudioProfiles))
{
Parent().AccMan().RemoveAudioRequest(AccInfo().iAddr);
Parent().ChangeStateL(CBasrvAccStateDetach::NewL(Parent()));
}
}
void CBasrvAccStateAttached::OpenAudioL(TAccAudioType aType)
{
TRACE_FUNC
TInt requestId = (aType == EAccMonoAudio)
? KRequestIdMonoActiveModeRequest : KRequestIdStereoActiveModeRequest;
if (!iAudioOpener)
{
iAudioOpener = CBasrvActive::NewL(*this, CActive::EPriorityLow, requestId);
}
else
{
if (iAudioOpener->IsActive())
{
LEAVE(KErrAlreadyExists);
}
iAudioOpener->SetRequestId(requestId);
}
Parent().RequestActiveMode();
// here we'll give time to the scheduler so that the active mode request has
// a chance to get through before the audio link open request - running
// with EPriorityLow for that
TRequestStatus* myStatus( &iAudioOpener->iStatus );
*myStatus = KRequestPending;
iAudioOpener->GoActive();
User::RequestComplete( myStatus, KErrNone );
}
void CBasrvAccStateAttached::CloseAudioL(TAccAudioType aType)
{
TRACE_FUNC
DoCloseAudioL(aType);
}
void CBasrvAccStateAttached::RequestCompletedL(CBasrvActive& aActive)
{
TRACE_FUNC
TInt status = aActive.iStatus.Int();
TInt requestid = aActive.RequestId();
switch (requestid)
{
case KRequestIdOpenMonoAudio:
{
if (!status)
{
if (AccInfo().iSuppProfiles & EHFP)
NewProfileConnection(EHFP);
else
NewProfileConnection(EHSP);
}
TInt err = Parent().AccMan().OpenAudioCompleted(AccInfo().iAddr, EAccMonoAudio, status);
if ( (err && err != KErrAlreadyExists) || (status && status != KErrInUse && status != KErrAlreadyExists))
{
if (Parent().AccMan().DisconnectIfAudioOpenFails())
{
Parent().AccMan().RemoveAudioRequest(AccInfo().iAddr);
Parent().ChangeStateL(CBasrvAccStateDetach::NewL(Parent()));
}
}
else
{
UpdateAudioState(EAudioLinkOpen, EAccMonoAudio);
Parent().CancelPowerModeControl();
AccInfo().iAudioOpenedProfiles |= (AccInfo().iConnProfiles & EAnyMonoAudioProfiles);
Parent().NotifyLinkChange2Rvc();
}
break;
}
case KRequestIdCloseMonoAudio:
{
UpdateAudioState(EAudioLinkClosed, EAccMonoAudio);
Parent().RequestSniffMode();
(void) Parent().AccMan().CloseAudioCompleted(AccInfo().iAddr, EAccMonoAudio, status);
break;
}
case KRequestIdOpenStereoAudio:
{
if (!status)
{
NewProfileConnection(EStereo);
}
TInt err = Parent().AccMan().OpenAudioCompleted(AccInfo().iAddr, EAccStereoAudio, status);
if ((status && status != KErrInUse && status != KErrAlreadyExists))
{
if (status == KErrDisconnected)
{
ProfileDisconnected(EStereo);
}
if (Parent().AccMan().DisconnectIfAudioOpenFails())
{
Parent().AccMan().RemoveAudioRequest(AccInfo().iAddr);
Parent().ChangeStateL(CBasrvAccStateDetach::NewL(Parent()));
}
}
else if (!err)
{
UpdateAudioState(EAudioLinkOpen, EAccStereoAudio);
Parent().PreventLowPowerMode();
AccInfo().iAudioOpenedProfiles |= EStereo;
Parent().NotifyLinkChange2Rvc();
DoConnectRemConCtIfNeededL();
}
break;
}
case KRequestIdCloseStereoAudio:
{
UpdateAudioState(EAudioLinkClosed, EAccStereoAudio);
Parent().RequestSniffMode();
(void) Parent().AccMan().CloseAudioCompleted(AccInfo().iAddr, EAccStereoAudio, status);
break;
}
case KRequestIdTimer:
{
TAccAudioType type = (iCloseAudioProfile & EAnyMonoAudioProfiles)
? EAccMonoAudio : EAccStereoAudio;
Parent().AccMan().AccfwConnectionL()->NotifyAudioLinkCloseL(AccInfo().iAddr, type);
// this variable needs to be cleared here so that the AccFW isn't notified a second time
// about the same audio link close during disconnection process
iCloseAudioProfile = EUnknownProfile;
break;
}
case KRequestIdMonoActiveModeRequest:
case KRequestIdStereoActiveModeRequest:
{
// the active mode request should have been scheduled at this point and we'll continue
// with audio link open request - going back to standard priority
TProfiles profile = ( requestid == KRequestIdMonoActiveModeRequest ) ? EHFP : EStereo;
Parent().AccMan().PluginMan().Plugin(profile)->OpenAudioLink(AccInfo().iAddr,
iAudioOpener->iStatus);
TInt requestId = ( requestid == KRequestIdMonoActiveModeRequest )
? KRequestIdOpenMonoAudio : KRequestIdOpenStereoAudio;
iAudioOpener->SetPriority( CActive::EPriorityStandard );
iAudioOpener->SetRequestId( requestId );
iAudioOpener->GoActive();
break;
}
case KRequestIdConnectRemConTG:
{
if (!status || status == KErrAlreadyExists)
{
NewProfileConnection(ERemConTG);
Parent().NotifyLinkChange2Rvc();
}
delete iRemConTGConnector;
iRemConTGConnector = NULL;
break;
}
default:
{
}
}
}
void CBasrvAccStateAttached::CancelRequest(CBasrvActive& aActive)
{
TRACE_FUNC
TInt request = aActive.RequestId();
TAccAudioType type = (request == KRequestIdOpenMonoAudio || request == KRequestIdCloseMonoAudio ) ?
EAccMonoAudio : EAccStereoAudio;
TProfiles profile = (type == EAccMonoAudio) ? EAnyMonoAudioProfiles : EStereo;
CBTAccPlugin* plugin = Parent().AccMan().PluginMan().Plugin(profile);
if (request == KRequestIdOpenMonoAudio || request == KRequestIdOpenStereoAudio)
{
plugin->CancelOpenAudioLink(AccInfo().iAddr);
Parent().AccMan().OpenAudioCompleted(AccInfo().iAddr, type, KErrCancel);
}
else if (request == KRequestIdCloseMonoAudio || request == KRequestIdCloseStereoAudio)
{
plugin->CancelCloseAudioLink(AccInfo().iAddr);
Parent().AccMan().CloseAudioCompleted(AccInfo().iAddr, type, KErrCancel);
}
else if (request == KRequestIdTimer)
{
iTimer.Cancel();
}
else if (request == KRequestIdConnectRemConTG)
{
Parent().AccMan().PluginMan().Plugin(ERemConTG)->CancelConnectToAccessory(AccInfo().iAddr);
}
}
CBasrvAccStateAttached::CBasrvAccStateAttached(CBasrvAcc& aParent, TBool aShowNote)
: CBasrvAccState(aParent), iShowNote(aShowNote)
{
}
void CBasrvAccStateAttached::HandleAccOpenedAudio(TProfiles aProfile)
{
TRACE_FUNC
TAccAudioType type = (aProfile & EAnyMonoAudioProfiles) ? EAccMonoAudio : EAccStereoAudio;
TInt latency = Parent().AccMan().PluginMan().AudioLinkLatency();
TInt ret = Parent().AccMan().NotifyAccFwAudioOpened(AccInfo().iAddr, type, latency);
if (ret == KErrNone)
{
UpdateAudioState(EAudioLinkOpen, (aProfile == EStereo) ? EAccStereoAudio : EAccMonoAudio);
CBasrvAccState::AccOpenedAudio(aProfile);
Parent().NotifyLinkChange2Rvc();
if (type == EAccStereoAudio)
{
Parent().RequestActiveMode();
Parent().PreventLowPowerMode();
Parent().AccMan().PluginMan().Plugin(EStereo)->StartRecording();
TRAP_IGNORE(DoConnectRemConCtIfNeededL());
}
else
{
Parent().CancelPowerModeControl();
}
}
StatePrint(_L("Attached"));
}
void CBasrvAccStateAttached::DoCloseAudioL(TAccAudioType aType)
{
TRACE_FUNC
TAccAudioType type = (iCloseAudioProfile & EAnyMonoAudioProfiles) ? EAccMonoAudio : EAccStereoAudio;
if ( type == aType && iTimerActive && iTimerActive->IsActive())
{
TRACE_STATE(_L("[BTAccStateAttached] Timer is cancelled"))
iTimerActive->Cancel();
Parent().AccMan().CloseAudioCompleted(AccInfo().iAddr, type, KErrNone);
return;
}
TInt requestId = (aType == EAccMonoAudio) ? KRequestIdCloseMonoAudio : KRequestIdCloseStereoAudio;
if (!iAudioCloser)
{
iAudioCloser = CBasrvActive::NewL(*this, CActive::EPriorityStandard, requestId);
}
else
{
if (iAudioCloser->IsActive())
{
LEAVE(KErrAlreadyExists);
}
iAudioCloser->SetRequestId(requestId);
}
TProfiles profile = (aType == EAccMonoAudio) ? EAnyMonoAudioProfiles : EStereo;
AccInfo().iAudioOpenedProfiles &= ~profile;
Parent().NotifyLinkChange2Rvc();
Parent().AccMan().PluginMan().Plugin(profile)->CloseAudioLink(AccInfo().iAddr, iAudioCloser->iStatus);
iAudioCloser->GoActive();
}
void CBasrvAccStateAttached::DoConnectRemConCtIfNeededL()
{
TRACE_FUNC
if (Parent().AccMan().IsAvrcpVolCTSupported() &&
IsAvrcpTGCat2SupportedByRemote() &&
!(AccInfo().iConnProfiles & ERemConTG) &&
!iRemConTGConnector)
{
iRemConTGConnector = CBasrvActive::NewL(*this, CActive::EPriorityStandard, KRequestIdConnectRemConTG);
CBTAccPlugin* plugin = Parent().AccMan().PluginMan().Plugin(ERemConTG);
TRACE_ASSERT(plugin, KErrNotFound)
plugin->ConnectToAccessory(AccInfo().iAddr, iRemConTGConnector->iStatus);
iRemConTGConnector->GoActive();
}
}
void CBasrvAccStateAttached::UpdateAudioState(TBTAudioLinkState aState, TAccAudioType aType)
{
TBTAudioLinkInfo info;
info.iAddr = AccInfo().iAddr;
info.iProfile = (aType == EAccMonoAudio) ? 0 : 1;
info.iState = aState;
TPckgBuf<TBTAudioLinkInfo> pkg(info);
RProperty::Set(KPSUidBluetoothEnginePrivateCategory, KBTAudioLinkStatus, pkg);
}