/*
* Copyright (c) 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:
*
*/
#include "atcodec.h"
#include <mmtsy_names.h> // for etel
#include <btengconstants.h>
#include <btengdiscovery.h>
#include <badesca.h>
#include "btmcprotocol.h"
#include "btmccallinghandler.h"
#include "btmcphonestatus.h"
#include "btmccallstatus.h"
#include "btmcprotocolstatus.h"
#include "btmcobserver.h"
#include "btmcmobileline.h"
#include "btmcnumber.h"
#include "btmcoperator.h"
#include "debug.h"
// Service Level Monitoring
const TInt KCmerFromHF = 0x01;
const TInt KCmerFromAG = 0x02;
const TInt KSLC_No3WayCalling = KCmerFromHF | KCmerFromAG;
const TInt KChldFromHF = 0x04;
const TInt KChldFromAG = 0x08;
const TInt KSLC_3WayCalling = KChldFromHF | KChldFromAG;
const TInt KBerUnknown = 99;
const TInt KServiceSlcTimer = 1;
const TInt KServiceSdpQuery = 2;
const TInt KServiceGetSubscriber = 3;
const TInt KServiceGetOperator = 4;
const TInt KServiceCmdHandling = 5;
const TInt KQueryIMEI = 6;
const TInt KQueryIMSI = 7;
const TInt KServiceFirstHspCkpdTrap = 8;
CBtmcProtocol* CBtmcProtocol::NewL(
MBtmcObserver& aObserver, TBtmcProfileId aProfile, const TDesC8& aBTDevAddr, TBool aAccessoryInitiated)
{
CBtmcProtocol* self=new (ELeave) CBtmcProtocol(aObserver);
CleanupStack::PushL(self);
self->ConstructL(aProfile, aBTDevAddr, aAccessoryInitiated);
CleanupStack::Pop(self);
return self;
}
CBtmcProtocol::~CBtmcProtocol()
{
TRACE_FUNC_ENTRY
delete iCallingHandler;
delete iCmdHanldingActive;
delete iAtExt;
delete iTimerActive;
iTimer.Close();
delete iProtocolStatus;
delete iCallStatus;
delete iPhoneStatus;
delete iNumber;
delete iOperator;
delete iEtelQuery;
delete iBteng;
iPhone.Close();
iServer.UnloadPhoneModule(KMmTsyModuleName);
iServer.Close();
delete iOutgoPacketQueue;
TRACE_FUNC_EXIT
}
void CBtmcProtocol::NewProtocolDataL(const TDesC8& aData)
{
TRACE_FUNC
TInt err = iInDataBuf.Append(aData);
if (err)
{
CATResult* nok = CATResult::NewLC(EATERROR);
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
}
else if (!iCmdHanldingActive->IsActive())
{
TRequestStatus* sta = &iCmdHanldingActive->iStatus;
*sta = KRequestPending;
User::RequestComplete(sta, KErrNone);
iCmdHanldingActive->GoActive();
}
}
void CBtmcProtocol::SendResponseL(const CATResult& aResult)
{
TRACE_FUNC
iOutgoPacketQueue->InsertL(0, aResult.Des());
iCredit = iOutgoPacketQueue->MdcaCount();
DoSendProtocolDataL();
}
void CBtmcProtocol::SendResponseL(const RPointerArray<CATResult>& aResults)
{
TRACE_FUNC
TInt count = aResults.Count();
for (TInt i = 0; i < count; i++)
{
iOutgoPacketQueue->InsertL(i, aResults[i]->Des());
}
iCredit = iOutgoPacketQueue->MdcaCount();
DoSendProtocolDataL();
}
void CBtmcProtocol::SendUnsoltResultL(const CATResult& aResult)
{
TRACE_FUNC_ENTRY
if (aResult.Id() == EATVGS)
{
iOutgoPacketQueue->InsertL(0, aResult.Des());
iCredit++;
}
else
{
iOutgoPacketQueue->AppendL(aResult.Des());
if (!iCallingHandler->ActiveCmdHandling() && !iHandleCmdPending)
{
iCredit++;
}
}
DoSendProtocolDataL();
TRACE_FUNC_EXIT
}
void CBtmcProtocol::CmdHandlingCompletedL()
{
TRACE_FUNC
iHandleCmdPending = EFalse;
TRequestStatus* sta = &iCmdHanldingActive->iStatus;
*sta = KRequestPending;
User::RequestComplete(sta, KErrNone);
iCmdHanldingActive->GoActive();
}
TBtmcProtocolStatus& CBtmcProtocol::ProtocolStatus()
{
return *iProtocolStatus;
}
void CBtmcProtocol::VoiceRecognitionError()
{
iPhoneStatus->SetRecognitionInitiator(EBTMonoVoiceRecognitionDefaultInitiator);
}
void CBtmcProtocol::HandleNrecCompletedL(TInt aErr)
{
TATId id = aErr ? EATERROR : EATOK;
CATResult* nok = CATResult::NewLC(id);
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
CmdHandlingCompletedL();
}
void CBtmcProtocol::ActivateRemoteVolumeControl()
{
TRACE_FUNC
if (iPhoneStatus)
iPhoneStatus->ActivateRemoteVolumeControl();
else
{
TRACE_INFO(_L("WARNING, null vol handler!"))
}
}
void CBtmcProtocol::DeActivateRemoteVolumeControl()
{
TRACE_FUNC
if (iPhoneStatus)
iPhoneStatus->DeActivateRemoteVolumeControl();
else
{
TRACE_INFO(_L("WARNING, null vol handler!"))
}
}
TInt CBtmcProtocol::GetRemoteSupportedFeature()
{
return iProtocolStatus->iAccSupportedFeature;
}
TBool CBtmcProtocol::ActiveChldHandling() const
{
return iCallingHandler->ActiveChldHandling();
}
void CBtmcProtocol::RequestCompletedL(CBtmcActive& aActive, TInt aErr)
{
TRACE_FUNC_ENTRY
switch (aActive.ServiceId())
{
case KServiceSlcTimer:
{
if (aErr == KErrNone)
{
StopTimer(KServiceSlcTimer);
iObserver.SlcIndicateL(EFalse);
}
break;
}
case KServiceSdpQuery:
{
}
case KServiceGetSubscriber:
{
delete iNumber;
iNumber = NULL;
CmdHandlingCompletedL();
break;
}
case KServiceGetOperator:
{
delete iOperator;
iOperator = NULL;
CmdHandlingCompletedL();
break;
}
case KServiceCmdHandling:
{
TRAPD(err, DoHandleCommandL());
if (err)
{
CATResult* nok = CATResult::NewLC(EATERROR);
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
CmdHandlingCompletedL();
}
break;
}
case KQueryIMSI:
{
TBuf8<RMobilePhone::KIMSISize> buf;
buf.Copy(iId);
CATResult* cimi = CATResult::NewLC(EATCIMI, EATReadResult, TATParam(buf));
SendResponseL(*cimi);
CleanupStack::PopAndDestroy(cimi);
CATResult* ok = CATResult::NewLC(EATOK);
SendResponseL(*ok);
CleanupStack::PopAndDestroy(ok);
CmdHandlingCompletedL();
break;
}
case KServiceFirstHspCkpdTrap:
{
// No handling here. From now on, any incoming CKPD command
// will be processed normally.
TRACE_INFO((_L("CKPD trapper terminated")));
break;
}
default:
break;
}
TRACE_FUNC_EXIT
}
void CBtmcProtocol::CancelRequest(TInt aServiceId)
{
TRACE_FUNC_ENTRY
if (aServiceId == KServiceSlcTimer || aServiceId == KServiceFirstHspCkpdTrap )
{
iTimer.Cancel();
}
else if (aServiceId == KServiceSdpQuery)
{
iBteng->CancelRemoteSdpQuery();
}
TRACE_FUNC_EXIT
}
CBtmcProtocol::CBtmcProtocol(MBtmcObserver& aObserver)
: iObserver(aObserver),
iSlcMask(0x00)
{
}
void CBtmcProtocol::ConstructL(TBtmcProfileId aProfile, const TDesC8& aBTDevAddr, TBool aAccessoryInitiated)
{
TRACE_FUNC_ENTRY
iProtocolStatus = new (ELeave) TBtmcProtocolStatus();
iProtocolStatus->iProfile = aProfile;
iAccessoryInitiated = aAccessoryInitiated;
iProtocolStatus->iAGSupportedFeature = KBTAGSupportedFeatureV15;
LEAVE_IF_ERROR(iServer.Connect())
TInt err = iServer.LoadPhoneModule(KMmTsyModuleName);
if (err != KErrNone && err != KErrAlreadyExists)
LEAVE(err);
LEAVE_IF_ERROR(iPhone.Open(iServer, KMmTsyPhoneName))
iCallStatus = CBtmcCallStatus::NewL(*this, iPhone, aProfile);
iProtocolStatus->iCallBits = iCallStatus->CallStatusL();
iEtelQuery = CBtmcActive::NewL(*this, CActive::EPriorityStandard, KQueryIMEI);
iPhone.GetPhoneId(iEtelQuery->iStatus, iIdentity);
iEtelQuery->GoActive();
switch (aProfile)
{
case EBtmcHFP0105:
{
TRACE_INFO((_L("constructing option HFP 1.5")))
iPhoneStatus = CBtmcPhoneStatus::NewL(*this, iPhone, aProfile);
StartTimerL(KServiceSlcTimer, KSlcTimeout);
break;
}
case EBtmcHSP:
{
TRACE_INFO((_L("constructing option HSP")))
if (aBTDevAddr.Length() != KBTDevAddrSize)
{
LEAVE(KErrBadDescriptor);
}
iProtocolStatus->iSlc = ETrue;
iBteng = CBTEngDiscovery::NewL(this);
iBteng->RemoteSdpQuery(TBTDevAddr(aBTDevAddr), TUUID(EBTProfileHSP), KBTHSRemoteAudioVolumeControl);
if ( iAccessoryInitiated && ( iProtocolStatus->iCallBits & KCallConnectedBit ) )
{
TRACE_INFO((_L("Incoming HSP connected, start CKPD trapper")));
// In case of incoming HSP connection during a voice call,
// start timer to trap the CKPD command as part of the audio transfer of HSP.
// If the first CKPD command is received during this period, it will be ignored.
StartTimerL(KServiceFirstHspCkpdTrap, KFirstHspCkpdTimeout);
}
break;
}
default:
LEAVE(KErrArgument);
}
TRAP_IGNORE(iAtExt = CHFPAtCmdHandler::NewL(*this));
iOutgoPacketQueue = new (ELeave) CDesC8ArrayFlat(1);
iCallingHandler = CBtmcCallingHandler::NewL(*this);
iCmdHanldingActive = CBtmcActive::NewL(*this, CActive::EPriorityStandard, KServiceCmdHandling);
if (iProtocolStatus->iSlc)
{
iCallStatus->ReportCallStatusL();
}
iVolumeSyncFromAccessory = EFalse;
TRACE_FUNC_EXIT
}
void CBtmcProtocol::DoHandleCommandL()
{
TRACE_FUNC
if (iHandleCmdPending)
{
return;
}
TBuf8<KMaxATSize> cmddes;
if (iInDataBuf.NextCommand(cmddes))
{
return;
}
if (cmddes.Length() == 0)
{
return;
}
TRACE_INFO_SEG(
{
TBuf8<KMaxATSize> buf;
buf = cmddes;
buf.Trim();
Trace(_L8("[HFP] [I] %S"), &buf);
})
CATCommand* cmd = NULL;
TRAPD(err, cmd = CATCommand::NewL(cmddes));
if (err)
{
if(iAtExt)
{
iAtExt->HandleCommand(cmddes, _L8("\n\rERROR\n\r"));
return;
}
CATResult* nok = CATResult::NewLC(EATERROR);
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
return;
}
CleanupStack::PushL(cmd);
iHandleCmdPending = ETrue;
TATId id = cmd->Id();
if (id == EATA ||
id == EATD2 ||
id == EATD1 ||
(id == EATCHLD && cmd->Type() != EATTestCmd)||
id == EATCHUP ||
id == EATVTS ||
(id == EATBVRA && cmd->Type() == EATWriteCmd) ||
id == EATBLDN )
{
if (id == EATBVRA)
{
TRACE_ASSERT(cmd->ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(cmd->Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
TBTMonoVoiceRecognitionInitiator initor;
initor = (value) ? EBTMonoVoiceRecognitionActivatedByRemote
: EBTMonoVoiceRecognitionDeactivatedByRemote;
iPhoneStatus->SetRecognitionInitiator(initor);
}
iCallingHandler->HandleCallingCmdL(*cmd);
}
else if ( id == EATCKPD )
{
if ( iTimerActive && iTimerActive->IsActive() )
{
// incoming HSP was just connected and the first CKPD is received,
// ignore this command as audio establishment will be done via Acc FW
StopTimer( KServiceFirstHspCkpdTrap );
TRACE_INFO((_L("First CKPD, do nothing")));
CATResult* nok = CATResult::NewLC( EATOK );
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
CmdHandlingCompletedL();
}
else
{
iCallingHandler->HandleCallingCmdL(*cmd);
}
}
else if (id == EATNREC && cmd->Type() == EATWriteCmd)
{
TRACE_ASSERT(cmd->ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(cmd->Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
if (!value &&
ServiceLevelConnected() )
{
//Always respond OK to +NREC=0
HandleNrecCompletedL(0);
}
else
{
LEAVE(KErrArgument);
}
}
else
{
// for HSP profile we support only VGS & VGM commands
// CKPD is handled elsewhere
if(iProtocolStatus->iProfile == EBtmcHSP &&
((cmd->Id() != EATVGS &&
cmd->Id() != EATVGM)) )
{
LEAVE(KErrNotSupported);
}
switch (cmd->Type())
{
case EATTestCmd:
{
HandleTestCommandL(*cmd);
break;
}
case EATReadCmd:
{
HandleReadCommandL(*cmd);
break;
}
case EATWriteCmd:
{
HandleWriteCommandL(*cmd);
break;
}
case EATActionCmd:
{
HandleActionCommandL(*cmd);
break;
}
default:
LEAVE(KErrNotSupported);
}
}
CleanupStack::PopAndDestroy(cmd);
if (!iProtocolStatus->iSlc && ServiceLevelConnected())
{
StopTimer(KServiceSlcTimer);
if(iPhoneStatus)
{
iPhoneStatus->SetVolumeControlFeatureL(iProtocolStatus->iAccSupportedFeature & KHfFeatureBitVolumeControl);
iPhoneStatus->SetVoiceRecognitionControlL(iProtocolStatus->iAccSupportedFeature & KHfFeatureBitVoiceRecognition);
}
iProtocolStatus->iSlc = ETrue;
iCallStatus->ReportCallStatusL();
iObserver.SlcIndicateL(ETrue);
}
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::HandleTestCommand
//
// Test Command handled by this method:
// CHLD, BVRA, CIND, CCWA, CLIP
// -----------------------------------------------------------------------------
//
void CBtmcProtocol::HandleTestCommandL(const CATCommand& aCmd)
{
TRACE_FUNC
RATResultPtrArray resarr;
ATObjArrayCleanupResetAndDestroyPushL(resarr);
TATParam param;
switch (aCmd.Id())
{
case EAT:
{
break;
}
case EATCHLD:
{
param = TATParam(KDesTestCodeCHLDv15);
iSlcMask = KSLC_3WayCalling;
break;
}
case EATBVRA:
{
param = TATParam(KDesTestCodeBVRA);
break;
}
case EATCIND:
{
param = TATParam(KDesTestCodeCINDv15);
break;
}
case EATCCWA:
{
param = TATParam(KDesTestCodeCCWA);
break;
}
case EATCLIP:
{
param = TATParam(KDesTestCodeCLIP);
break;
}
case EATCREG:
{
param = TATParam(KDesTestCodeCCWA); // it is the same (0,1)
break;
}
case EATCOLP:
{
param = TATParam(KDesTestCodeCCWA); // it is the same (0,1)
break;
}
default:
LEAVE(KErrNotFound);
}
if (param.Type() != EATNullParam)
{
CATResult* code = CATResult::NewL(aCmd.Id(), EATTestResult, param);
CleanupStack::PushL(code);
resarr.AppendL(code);
CleanupStack::Pop(code);
}
CATResult* ok = CATResult::NewL(EATOK);
CleanupStack::PushL(ok);
resarr.AppendL(ok);
CleanupStack::Pop(ok);
SendResponseL(resarr);
CleanupStack::PopAndDestroy(&resarr);
CmdHandlingCompletedL();
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::HandleReadCommand
//
// Read Command handled by this method:
// CIND, CLIP, COPS
// -----------------------------------------------------------------------------
//
void CBtmcProtocol::HandleReadCommandL(const CATCommand& aCmd)
{
TRACE_FUNC
CATResult* code = NULL;
RArray<TATParam> params;
CleanupClosePushL(params);
TBool response = EFalse;
switch (aCmd.Id())
{
case EATCIND:
{
// Network status
if(!iPhoneStatus)
{
LEAVE(KErrNotSupported);
}
RMobilePhone::TMobilePhoneRegistrationStatus net =
iPhoneStatus->NetworkStatus();
if (net == RMobilePhone::ERegisteredOnHomeNetwork ||
net == RMobilePhone::ERegisteredRoaming)
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoATNetworkAvailable)))
}
else
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoATNetworkUnavailable)))
}
// call status
if ((iProtocolStatus->iCallBits & KCallConnectedBit) ||
(iProtocolStatus->iCallBits & KCallHoldBit))
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoATCallActive)))
}
else
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoATNoCall)))
}
// Call setup status
TInt callSetupInd = EBTMonoATNoCallSetup;
if (iProtocolStatus->iCallBits & KCallRingingBit)
{
callSetupInd = EBTMonoATCallRinging;
}
else if (iProtocolStatus->iCallBits & KCallDiallingBit)
{
callSetupInd = EBTMonoATCallDialling;
}
else if (iProtocolStatus->iCallBits & KCallConnectingBit)
{
callSetupInd = EBTMonoATCallConnecting;
}
LEAVE_IF_ERROR(params.Append(TATParam(callSetupInd)))
// call_setup == callsetup
LEAVE_IF_ERROR(params.Append(TATParam(callSetupInd)))
// Call held status
TInt callHeldInd = EBTMonoATNoCallHeld;
if( (iProtocolStatus->iCallBits & KCallHoldBit) && (iProtocolStatus->iCallBits & KCallConnectedBit) )
{
callHeldInd = EBTMonoATCallHeldAndActive;
}
else if(iProtocolStatus->iCallBits & KCallHoldBit)
{
callHeldInd = EBTMonoATCallHeldOnly;
}
LEAVE_IF_ERROR(params.Append(TATParam(callHeldInd)))
// signal status
LEAVE_IF_ERROR(params.Append(TATParam(iPhoneStatus->GetSignalStrength())))
// roaming status
if(net == RMobilePhone::ERegisteredRoaming)
{
LEAVE_IF_ERROR(params.Append(TATParam(1)))
}
else
{
LEAVE_IF_ERROR(params.Append(TATParam(0)))
}
// battery charge
LEAVE_IF_ERROR(params.Append(TATParam(iPhoneStatus->GetBatteryCharge())))
code = CATResult::NewL(EATCIND, EATReadResult, ¶ms);
response = ETrue;
break;
}
case EATCLIP:
{
LEAVE_IF_ERROR(params.Append(TATParam(iProtocolStatus->iCallerIdNotif)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoATCallerIdNetworkServiceUnknown)))
code = CATResult::NewL(EATCLIP, EATReadResult, ¶ms);
response = ETrue;
break;
}
case EATCOPS:
{
iOperator = CBtmcOperator::NewL(*this, *this, CActive::EPriorityStandard, KServiceGetOperator);
iOperator->GoActive();
break;
}
case EATCREG:
{
if(!iPhoneStatus)
{
LEAVE(KErrNotSupported);
}
RMobilePhone::TMobilePhoneRegistrationStatus net =
iPhoneStatus->NetworkStatus();
response = ETrue;
switch(net)
{
case RMobilePhone::ERegistrationUnknown:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceUnknown)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
case RMobilePhone::ENotRegisteredEmergencyOnly:
case RMobilePhone::ENotRegisteredNoService:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceNotRegistered)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
case RMobilePhone::ENotRegisteredSearching:
case RMobilePhone::ERegisteredBusy:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceNotRegisteredSearching)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
case RMobilePhone::ERegisteredOnHomeNetwork:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceHomeNetwork)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
case RMobilePhone::ERegistrationDenied:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceRegistrationDenied)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
case RMobilePhone::ERegisteredRoaming:
{
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregEnableUnsolicited)))
LEAVE_IF_ERROR(params.Append(TATParam(EBTMonoCregNetworkServiceRegisteredRoaming)))
code = CATResult::NewL(EATCREG, EATReadResult, ¶ms);
break;
}
default:
TRACE_INFO(_L("Error: default in CREG"));
break;
};
break;
}
case EATCSQ:
{
TRACE_INFO(_L("Requesting Signal strength"));
response = ETrue;
LEAVE_IF_ERROR(params.Append(TATParam(iPhoneStatus->GetRssiStrength())))
LEAVE_IF_ERROR(params.Append(TATParam(KBerUnknown)))
code = CATResult::NewL(EATCSQ, EATReadResult, ¶ms);
TRACE_INFO(_L("done"));
break;
}
case EATCGSN:
{
response = ETrue;
TBuf8<RMobilePhone::KPhoneSerialNumberSize> buf;
buf.Copy(iIdentity.iSerialNumber);
LEAVE_IF_ERROR(params.Append(TATParam(buf)))
code = CATResult::NewL(EATCGSN, EATReadResult, ¶ms);
break;
}
case EATCGMI:
{
response = ETrue;
TBuf8<RMobilePhone::KPhoneManufacturerIdSize> buf;
buf.Copy(iIdentity.iManufacturer);
LEAVE_IF_ERROR(params.Append(TATParam(buf)))
code = CATResult::NewL(EATCGMI, EATReadResult, ¶ms);
break;
}
case EATCGMM:
{
response = ETrue;
TBuf8<RMobilePhone::KPhoneModelIdSize> buf;
buf.Copy(iIdentity.iModel);
LEAVE_IF_ERROR(params.Append(TATParam(buf)))
code = CATResult::NewL(EATCGMM, EATReadResult, ¶ms);
break;
}
case EATCGMR:
{
response = ETrue;
TBuf8<RMobilePhone::KPhoneRevisionIdSize> buf;
buf.Copy(iIdentity.iRevision);
LEAVE_IF_ERROR(params.Append(TATParam(buf)))
code = CATResult::NewL(EATCGMR, EATReadResult, ¶ms);
break;
}
case EATCIMI:
{
iEtelQuery->SetServiceId(KQueryIMSI);
iPhone.GetSubscriberId(iEtelQuery->iStatus, iId);
iEtelQuery->GoActive();
break;
}
case EATCOLP:
{
response = ETrue;
LEAVE_IF_ERROR(params.Append(TATParam(TInt(iProtocolStatus->iOutgoingCallNotif))))
code = CATResult::NewL(EATCOLP, EATReadResult, ¶ms);
break;
}
default:
LEAVE(KErrNotSupported);
}
CleanupStack::PopAndDestroy(¶ms);
if (response)
{
RATResultPtrArray resarr;
CleanupStack::PushL(code);
ATObjArrayCleanupResetAndDestroyPushL(resarr);
resarr.AppendL(code);
CleanupStack::Pop(code);
CATResult* ok = CATResult::NewL(EATOK);
CleanupStack::PushL(ok);
resarr.AppendL(ok);
CleanupStack::Pop(ok);
SendResponseL(resarr);
CleanupStack::PopAndDestroy(&resarr);
CmdHandlingCompletedL();
}
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::HandleWriteCommand
//
// Write Command handled by this method:
// CHLD BVRA, CCWA, CLIP, VGS, VGM, CMEE, COPS
// -----------------------------------------------------------------------------
//
void CBtmcProtocol::HandleWriteCommandL(const CATCommand& aCmd)
{
TRACE_FUNC
RATResultPtrArray resarr;
ATObjArrayCleanupResetAndDestroyPushL(resarr);
CATResult* code = NULL;
switch (aCmd.Id())
{
case EATCMER:
{
TRACE_ASSERT(aCmd.ParamNum() >= 4, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
if (value == 3)
{
param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(3, param))
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iIndicatorNotif = (value == 1) ?
KIndAllActivated : KIndAllDeActivated;
TRACE_INFO((_L("Indicators enabled: %d"), value))
}
iSlcMask = KSLC_No3WayCalling;
break;
}
case EATBRSF:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iAccSupportedFeature = value;
code = CATResult::NewL(EATBRSF, EATWriteResult,
TATParam(iProtocolStatus->iAGSupportedFeature));
break;
}
case EATCCWA:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iCallWaitingNotif = (TBTMonoATCallWaitingNotif)value;
break;
}
case EATCLIP:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iCallerIdNotif = (TBTMonoATCallerIdNotif) value ;
break;
}
case EATVGS:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
if (!iPhoneStatus)
{
iPhoneStatus = CBtmcPhoneStatus::NewL(*this, iPhone, iProtocolStatus->iProfile);
iPhoneStatus->SetVolumeControlFeatureL(ETrue);
}
if(iVolumeSyncFromAccessory)
{
iPhoneStatus->SetSpeakerVolumeL(value);
}
break;
}
case EATVGM:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
break;
}
case EATCMEE:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iAdvancedErrorCode = value;
break;
}
case EATCOPS:
{
break;
}
case EATBIA:
{
for(TInt i=0; i<aCmd.ParamNum(); i++)
{
TATParam param = TATParam();
aCmd.Parameter(i, param);
if(param.Type() == EATNullParam)
continue;
else
{
TInt value;
param.Int(value);
SetIndicatorL(i+1, value);
}
}
break;
}
case EATCREG:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iNetworkRegStatusNotif = TBool(value);
break;
}
case EATCOLP:
{
TRACE_ASSERT(aCmd.ParamNum() == 1, KErrArgument);
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
iProtocolStatus->iOutgoingCallNotif = TBool(value);
break;
}
default:
LEAVE(KErrNotSupported);
}
if (code)
{
CleanupStack::PushL(code);
resarr.AppendL(code);
CleanupStack::Pop(code);
}
CATResult* ok = CATResult::NewL(EATOK);
CleanupStack::PushL(ok);
resarr.AppendL(ok);
CleanupStack::Pop(ok);
SendResponseL(resarr);
CleanupStack::PopAndDestroy(&resarr);
// solution to volume sync - phone will always send its volume status back to accessory
if( (aCmd.Id() == EATVGS) && (iVolumeSyncFromAccessory == EFalse) )
{
iVolumeSyncFromAccessory = ETrue;
TInt vol(KErrNotFound);
if(iPhoneStatus)
{
vol = iPhoneStatus->GetVolumeStatus();
}
if(vol > KErrNotFound) // volume exists
{
TATParam param = TATParam();
LEAVE_IF_ERROR(aCmd.Parameter(0, param))
TInt value;
LEAVE_IF_ERROR(param.Int(value))
if(value != vol)
{
CATResult* event = CATResult::NewLC(EATVGS, EATUnsolicitedResult, vol);
SendUnsoltResultL(*event);
CleanupStack::PopAndDestroy(event);
}
}
}
CmdHandlingCompletedL();
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::HandleActionCommandL
//
// Action Command handled by this method:
// CLCC CNUM
// -----------------------------------------------------------------------------
//
void CBtmcProtocol::HandleActionCommandL(const CATCommand& aCmd)
{
TRACE_FUNC
switch (aCmd.Id())
{
case EATCNUM:
{
iNumber = CBtmcNumber::NewL(*this, *this, CActive::EPriorityStandard, KServiceGetSubscriber);
iNumber->GoActive();
break;
}
case EATCLCC:
{
iCallStatus->HandleClccL();
break;
}
case EATCGSN:
{
CATResult* code = NULL;
RArray<TATParam> params;
CleanupClosePushL(params);
TBuf8<RMobilePhone::KPhoneSerialNumberSize> buf;
buf.Copy(iIdentity.iSerialNumber);
LEAVE_IF_ERROR(params.Append(TATParam(buf)))
code = CATResult::NewL(EATCGSN, EATActionResult, ¶ms);
CleanupStack::PopAndDestroy(¶ms);
CleanupStack::PushL(code);
RATResultPtrArray resarr;
ATObjArrayCleanupResetAndDestroyPushL(resarr);
resarr.AppendL(code);
CleanupStack::Pop(code);
CATResult* ok = CATResult::NewL(EATOK);
CleanupStack::PushL(ok);
resarr.AppendL(ok);
CleanupStack::Pop(ok);
SendResponseL(resarr);
CleanupStack::PopAndDestroy(&resarr);
CmdHandlingCompletedL();
break;
}
default:
LEAVE(KErrNotSupported);
}
}
void CBtmcProtocol::DoSendProtocolDataL()
{
TRACE_INFO((_L("credit %d"), iCredit))
TInt count = iOutgoPacketQueue->MdcaCount();
for (TInt i = 0; iCredit >0 && i < count; i++)
{
iCredit--;
TBuf8<KMaxATSize> buf;
buf.Copy(iOutgoPacketQueue->MdcaPoint(0));
iObserver.SendProtocolDataL(buf);
iOutgoPacketQueue->Delete(0);
}
}
void CBtmcProtocol::StartTimerL(TInt aService, TInt aTimeout)
{
if (!iTimerActive)
{
TRACE_FUNC_ENTRY
iTimerActive = CBtmcActive::NewL(*this, CActive::EPriorityStandard, aService);
LEAVE_IF_ERROR(iTimer.CreateLocal());
iTimer.After(iTimerActive->iStatus, aTimeout);
iTimerActive->GoActive();
TRACE_FUNC_EXIT
}
else
{
TRACE_WARNING(_L("WARNING, timer is active already"))
}
}
void CBtmcProtocol::StopTimer(TInt aService)
{
if (iTimerActive && iTimerActive->ServiceId() == aService)
{
TRACE_FUNC_ENTRY
delete iTimerActive;
iTimerActive = NULL;
iTimer.Close();
TRACE_FUNC_EXIT
}
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::IsServiceLevelEstablished
// -----------------------------------------------------------------------------
//
TBool CBtmcProtocol::ServiceLevelConnected() const
{
TRACE_FUNC
TRACE_INFO((_L("SLC 0x%02x"), iSlcMask))
if ((iProtocolStatus->iAccSupportedFeature & 0x02) &&
(iProtocolStatus->iAGSupportedFeature & 0x01) &&
(iSlcMask == KSLC_3WayCalling))
{
return ETrue;
}
if ((!(iProtocolStatus->iAccSupportedFeature & 0x02) ||
!(iProtocolStatus->iAGSupportedFeature & 0x01)) &&
(iSlcMask == KSLC_No3WayCalling))
{
return ETrue;
}
return EFalse;
}
// -----------------------------------------------------------------------------
// CBtmcProtocol::ServiceAttributeSearchComplete
// -----------------------------------------------------------------------------
//
void CBtmcProtocol::ServiceAttributeSearchComplete( TSdpServRecordHandle /*aHandle*/,
const RSdpResultArray& aAttr,
TInt aErr )
{
TRACE_FUNC
if((aErr == KErrNone || aErr == KErrEof) && aAttr.Count() && aAttr[0].iAttrValue.iValNumeric)
{
TRACE_INFO((_L("Remote volume control supported")))
TInt err = KErrNone;
if (!iPhoneStatus)
{
TRAP(err, iPhoneStatus = CBtmcPhoneStatus::NewL(*this, iPhone, iProtocolStatus->iProfile));
}
if (err == KErrNone)
{
TRAP_IGNORE(iPhoneStatus->SetVolumeControlFeatureL(ETrue));
iPhoneStatus->ActivateRemoteVolumeControl();
}
}
if(aErr)
{
delete iBteng;
iBteng = NULL;
}
}
void CBtmcProtocol::SetIndicatorL(TInt aIndicator, TInt aValue)
{
TInt indBit( 0 );
switch(aIndicator)
{
case EBTMonoATNetworkIndicator:
indBit = KIndServiceBit;
break;
case EBTMonoATCallIndicator:
case EBTMonoATCallSetupIndicator:
case EBTMonoATCall_SetupIndicator:
case EBTMonoATCallHeldIndicator:
// call, call setup, call held indicators are mandatory.
break;
case EBTMonoATSignalStrengthIndicator:
indBit = KIndSignalBit;
break;
case EBTMonoATRoamingIndicator:
indBit = KIndRoamBit;
break;
case EBTMonoATBatteryChargeIndicator:
indBit = KIndChargeBit;
break;
default:
LEAVE(KErrArgument)
}
if ( indBit )
{
if ( aValue )
{
iProtocolStatus->iIndicatorNotif |= indBit;
}
else
{
iProtocolStatus->iIndicatorNotif &= ~indBit;
}
}
}
void CBtmcProtocol::UnsolicitedResultFromATExtL(TInt aErr, const TDesC8& aAT)
{
TRACE_FUNC
if(aErr)
{
delete iAtExt;
iAtExt = NULL;
}
else
{
iOutgoPacketQueue->AppendL(aAT);
if (!iCallingHandler->ActiveCmdHandling() && !iHandleCmdPending)
{
iCredit++;
}
DoSendProtocolDataL();
}
}
void CBtmcProtocol::ATExtHandleCommandCompletedL(TInt aErr, const TDesC8& aReply)
{
TRACE_FUNC
if(aErr)
{
CATResult* nok = CATResult::NewLC(EATERROR);
SendResponseL(*nok);
CleanupStack::PopAndDestroy(nok);
}
else
{
iOutgoPacketQueue->AppendL(aReply);
if (!iCallingHandler->ActiveCmdHandling() && !iHandleCmdPending)
{
iCredit++;
}
DoSendProtocolDataL();
}
}
// End of file