// Copyright (c) 2001-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:
// This file contains the implementation of the Simulator TSY Voice Call functionality.
// The Call classes process the Call-based requests made by ETel clients
// and passed down to the TSY by the ETel Server.
//
//
/**
@file
*/
#include <testconfigfileparser.h>
#include "CSimVoiceCall.h"
#include "CSimPhone.h"
#include "CSimDtmf.h"
#include "Simlog.h"
#include "CSimTsyMode.h"
CSimVoiceCall* CSimVoiceCall::NewL(CSimLine* aLine,const TDesC& aName, CSimPhone* aPhone)
/**
* Standard two phase constructor.
*
* @param aLine pointer to the Line object.
* @param aName name of the call to be constructed
* @return CSimVoiceCall pointer to the voice call object created
* @leave Leaves if no memory or object is not created for any reason
*/
{
CSimVoiceCall* voiceCall=new(ELeave) CSimVoiceCall(aLine,aName,aPhone);
TCleanupItem newCallVoiceClose(CloseCall,voiceCall);
CleanupStack::PushL(newCallVoiceClose);
voiceCall->ConstructL();
CleanupStack::Pop();
return voiceCall;
}
CSimVoiceCall::CSimVoiceCall(CSimLine* aLine,const TDesC& aName, CSimPhone* aPhone)
: CSimCall(aLine,aName,aPhone)
/**
* Trivial constructor. Calls CSimCall to initialise its members
*/
{
}
void CSimVoiceCall::ConstructL()
/**
* Second phase of 2-Phase Constructor
* Retrieves all the pausing duration tags from the config file
*
* @param aName name of the voice call to be constructed
*/
{
LOGVOICE1("Starting to parse Voice Call config parameters...");
iCaps=Caps();
iDiallingPause=iLine->CfgFile()->ItemValue(KDiallingPauseDuration,KDefaultDiallingPauseDuration);
iConnectingPause=iLine->CfgFile()->ItemValue(KConnectingPauseDuration,KDefaultConnectingPauseDuration);
iDisconnectingPause=iLine->CfgFile()->ItemValue(KDisconnectingPauseDuration,KDefaultDisconnectingPauseDuration);
iAnswerIncomingPause=iLine->CfgFile()->ItemValue(KAnswerIncomingPauseDuration,KDefaultAnswerIncomingPauseDuration);
iRemoteHangupPause=iLine->CfgFile()->ItemValue(KRemoteHangupPauseDuration,KDefaultRemoteHangupPauseDuration);
iTimer=CSimTimer::NewL(iLine->iPhone);
CSimCall::ConstructL();
//If present read in remote party info tag
TPtrC8 callingname, remotenumber;
TInt delay=0;
const CTestConfigItem* item=NULL;
item=iLine->CfgFile()->Item(KNotifyRemotePartyInfo);
TInt ret;
if (item)
{
ret=CTestConfig::GetElement(item->Value(),KStdDelimiter,0,delay);
if(ret!=KErrNone)
{
LOGPARSERR("delay",ret,0,&KNotifyRemotePartyInfo);
}
ret=CTestConfig::GetElement(item->Value(),KStdDelimiter,1,callingname);
if(ret!=KErrNone)
{
LOGPARSERR("callingname",ret,1,&KNotifyRemotePartyInfo);
}
ret=CTestConfig::GetElement(item->Value(),KStdDelimiter,2,remotenumber);
if(ret!=KErrNone)
{
LOGPARSERR("remotenumber",ret,2,&KNotifyRemotePartyInfo);
}
iNotifyRemotePartyInfoTimer->iDelay = delay;
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iDirection = RMobileCall::EDirectionUnknown;
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iCallingName.Copy(callingname);
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteNumber.iTelNumber.Copy(remotenumber);
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteNumber.iTypeOfNumber = RMobilePhone::EInternationalNumber;
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteNumber.iNumberPlan = RMobilePhone::EIsdnNumberPlan;
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteIdStatus = RMobileCall::ERemoteIdentityAvailable;
}
else
{
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteIdStatus = RMobileCall::ERemoteIdentityUnknown;
}
LOGVOICE1("...Finished parsing Voice Call config parameters...");
}
CSimVoiceCall::~CSimVoiceCall()
/**
* Destroy all the objects constructed.
* CSimTimer and CSimSysAgent objects are destroyed here
*/
{
delete iTimer;
if(iAnswerIncomingCall.iNotifyPending)
iLine->ResetAutoAnswerCallObject(this);
}
TInt CSimVoiceCall::ExtFunc(const TTsyReqHandle aTsyReqHandle,const TInt aIpc,const TDataPackage& aPackage)
/**
* ExtFunc is called by the server when it has a "extended", i.e. non-core ETel request
* for the TSY to process
* A request handle, request type and request data are passed to the TSY
*
* @param aTsyReqHandle
* @param aIpc IPc number representing the request
* @param aPackage data for the request
* @return KErrNone
*/
{
TAny* dataPtr=aPackage.Ptr1();
// The request data has to extracted from TDataPackage and the TAny* pointers have to
// be "cast" to the expected request data type
switch (aIpc)
{
//
// No Flow Control NOR Multiple Completion
//
case EMobileCallGetMobileCallCaps:
return GetMobileCallCaps(aTsyReqHandle,aPackage.Des1n());
case EMobileCallGetMobileCallStatus:
return GetMobileCallStatus(aTsyReqHandle,
REINTERPRET_CAST(RMobileCall::TMobileCallStatus*,dataPtr));
case EMobileCallGetMobileCallInfo:
return GetMobileCallInfo(aTsyReqHandle,aPackage.Des1n());
case EMobileCallDialEmergencyCall:
return DialEmergencyCall(aTsyReqHandle,aPackage.Des1u());
case EMobileCallHold:
return Hold(aTsyReqHandle);
case EMobileCallResume:
return Resume(aTsyReqHandle);
case EMobileCallSwap:
return Swap(aTsyReqHandle);
case EMobileCallDialISV:
return DialISV(aTsyReqHandle, aPackage.Des1n(), aPackage.Des2u());
case EMobileCallAnswerISV:
TInt retVal;
TRAPD(errVal, retVal=AnswerIncomingCallISVL(aTsyReqHandle, aPackage.Des1n()));
if (errVal != KErrNone)
{
return errVal;
}
else
{
return retVal;
}
//
// Multiple Completion Services with Immediate Server Repost
// (Usually Notifications)
//
case EMobileCallNotifyMobileCallStatusChange:
return NotifyMobileCallStatusChange(aTsyReqHandle,
REINTERPRET_CAST(RMobileCall::TMobileCallStatus*, dataPtr));
case EMobileCallNotifyMobileCallCapsChange:
return NotifyMobileCallCapsChange(aTsyReqHandle, aPackage.Des1n());
case EMobileCallNotifyRemotePartyInfoChange:
return NotifyRemotePartyInfoChange(aTsyReqHandle, aPackage.Des1n());
default:
break;
}
return KErrNotSupported;
}
TInt CSimVoiceCall::CancelService(const TInt aIpc,const TTsyReqHandle aTsyReqHandle)
/**
* Cancel an outstanding request.
* @param aIpc The IPC number of the request that must be cancelled. Note: this is not the
* IPC number of the cancel request itself.
* @param aTsyReqHandle The TSY Request Handle of the request to be cancelled.
*/
{
switch(aIpc)
{
case EMobileCallNotifyMobileCallStatusChange:
return NotifyMobileCallStatusChangeCancel(aTsyReqHandle);
case EMobileCallNotifyMobileCallCapsChange:
return NotifyMobileCallCapsChangeCancel(aTsyReqHandle);
case EMobileCallDialEmergencyCall:
return DialEmergencyCallCancel(aTsyReqHandle);
case EMobileCallHold:
return HoldCancel(aTsyReqHandle);
case EMobileCallResume:
return ResumeCancel(aTsyReqHandle);
case EMobileCallSwap:
return SwapCancel(aTsyReqHandle);
case EMobileCallNotifyRemotePartyInfoChange:
return NotifyRemotePartyInfoChangeCancel();
case EMobileCallDialISV:
return DialISVCancel(aTsyReqHandle);
case EMobileCallAnswerISV:
return AnswerIncomingCallISVCancel(aTsyReqHandle);
default:
break;
}
return CCallBase::CancelService(aIpc,aTsyReqHandle);
}
TInt CSimVoiceCall::DialEmergencyCall(const TTsyReqHandle aTsyReqHandle,TDesC* aTelNumber)
/**
* Process a client's emergency call request.
*
* @param aTsyReqHandle
* @param aTelNumber The telephone number to dial
* @return KErrNone
*/
{
return Dial(aTsyReqHandle,NULL,aTelNumber);
}
TInt CSimVoiceCall::DialEmergencyCallCancel(const TTsyReqHandle aTsyReqHandle)
/**
* Cancels an emergency call request.
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
return DialCancel(aTsyReqHandle);
}
TInt CSimVoiceCall::Dial(const TTsyReqHandle aTsyReqHandle,const TDesC8* aCallParams,TDesC* /*aTelNumber*/)
/**
* Process a client's dial request.
*
* @param aTsyReqHandle
* @param aCallParams the call parameters
* @param aTelNumber The telephone number to dial
* @return KErrNone
*/
{
LOGVOICE1(">>CSimVoiceCall::Dial");
// Note: The telephone number and call parameters should be validated against config file
// values here.
//see where the dial request has orignated from
if(aCallParams)
{
RCall::TCallParamsPckg* callparamsPckg=reinterpret_cast<RCall::TCallParamsPckg*>(const_cast<TDesC8*>(aCallParams));
RCall::TCallParams& callparams=(*callparamsPckg)();
if(callparams.ExtensionId() == RMobileCall::KETel3rdPartyCallParamsV1)
{
LOGVOICE1("<<CSimVoiceCall::Dial request from Etel 3rd Party client.");
}
}
iDialRequestHandle=aTsyReqHandle;
TInt ret;
ret = ActionEvent(ECallEventDial,KErrNone);
LOGVOICE1("<<CSimVoiceCall::Dial");
return ret;
}
TInt CSimVoiceCall::DialCancel(const TTsyReqHandle /*aTsyReqHandle*/)
/**
* Cancels a dial Request
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
LOGVOICE1(">>CSimVoiceCall::DialCancel");
switch(iState)
{
case RMobileCall::EStatusIdle:
SimPanic(EIllegalCancelRequest); // A DialCancel should never reach the TSY in this state.
break;
case RMobileCall::EStatusDialling:
case RMobileCall::EStatusConnecting:
iTimer->DoCancel();
// Can ignore for cancel
TInt err;
err = KErrNone;
TRAP(err, (void)ChangeStateL(RMobileCall::EStatusIdle,EFalse,EFalse));
if (err != KErrNone)
{
ReqCompleted(iDialRequestHandle, err);
}
else
{
ReqCompleted(iDialRequestHandle, KErrCancel);
}
break;
case RMobileCall::EStatusConnected:
SimPanic(EIllegalCancelRequest); // A DialCancel should never reach the TSY in this state.
break;
default:
break;
}
LOGVOICE1("<<CSimVoiceCall::DialCancel");
return KErrNone;
}
TInt CSimVoiceCall::DialISV(const TTsyReqHandle aTsyReqHandle,const TDesC8* aCallParams,TDesC* /*aTelNumber*/)
/**
* Process an EtelISV client's dial request.
*
* @param aTsyReqHandle
* @param aCallParams the call parameters
* @param aTelNumber The telephone number to dial
* @return KErrNone
*/
{
LOGVOICE1(">>CSimVoiceCall::DialISV");
// Note: The telephone number and call parameters should be validated against config file
// values here.
//see where the dial request has orignated from
if(aCallParams)
{
RCall::TCallParamsPckg* callparamsPckg=reinterpret_cast<RCall::TCallParamsPckg*>(const_cast<TDesC8*>(aCallParams));
RCall::TCallParams& callparams=(*callparamsPckg)();
if(callparams.ExtensionId() == RMobileCall::KETel3rdPartyCallParamsV1)
{
LOGVOICE1("<<CSimVoiceCall::Dial request from Etel 3rd Party client.");
}
}
iDialRequestHandle=aTsyReqHandle;
TInt ret;
ret = ActionEvent(ECallEventDial,KErrNone);
LOGVOICE1("<<CSimVoiceCall::DialISV");
return ret;
}
TInt CSimVoiceCall::DialISVCancel(const TTsyReqHandle /*aTsyReqHandle*/)
/**
* Cancels an EtelISV originated dial Request
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
LOGVOICE1(">>CSimVoiceCall::DialISVCancel");
switch(iState)
{
case RMobileCall::EStatusIdle:
SimPanic(EIllegalCancelRequest); // A DialCancel should never reach the TSY in this state.
break;
case RMobileCall::EStatusDialling:
case RMobileCall::EStatusConnecting:
iTimer->DoCancel();
// Can ignore for cancel
TInt err;
err = KErrNone;
TRAP(err, (void)ChangeStateL(RMobileCall::EStatusIdle,EFalse,EFalse));
if (err != KErrNone)
{
ReqCompleted(iDialRequestHandle, err);
}
else
{
ReqCompleted(iDialRequestHandle, KErrCancel);
}
break;
case RMobileCall::EStatusConnected:
SimPanic(EIllegalCancelRequest); // A DialCancel should never reach the TSY in this state.
break;
default:
break;
}
LOGVOICE1("<<CSimVoiceCall::DialISVCancel");
return KErrNone;
}
TInt CSimVoiceCall::AnswerIncomingCall(const TTsyReqHandle aTsyReqHandle,const TDesC8* aCallParams)
/**
* Register a client's interest in answering the next incoming call.
* First register interest in incoming calls with the line, then, if a call
* is already ringing, start the answer procedure.
*
* @param aTsyReqHandle
* @param aCallParams the call parameters
* @return KErrNone
*/
{
LOGVOICE3(">>CSimVoiceCall::AnswerIncomingCall 0x%08x, state %d entry ",this,iState);
//see where the answer request has orignated from
if(aCallParams)
{
RCall::TCallParamsPckg* callparamsPckg=reinterpret_cast<RCall::TCallParamsPckg*>(const_cast<TDesC8*>(aCallParams));
RCall::TCallParams& callparams=(*callparamsPckg)();
if(callparams.ExtensionId() == RMobileCall::KETel3rdPartyCallParamsV1)
{
LOGVOICE1("<<CSimVoiceCall::AnswerIncomingCall request from Etel 3rd Party client.");
}
}
TInt ret=iLine->SetAutoAnswerCallObject(this);
if(ret!=KErrNone)
{
ReqCompleted(aTsyReqHandle,ret);
return KErrNone;
}
iAnswerIncomingCall.iNotifyPending=ETrue;
iAnswerIncomingCall.iNotifyHandle=aTsyReqHandle;
if(iState==RMobileCall::EStatusRinging)
ret = ActionEvent(ECallEventAnswerIncoming,KErrNone);
else if(iLine->iState==RMobileCall::EStatusRinging)
{
TRAPD(errVal, ret=ChangeStateL(RMobileCall::EStatusRinging,EFalse,EFalse));
if (errVal != KErrNone)
{
ret = errVal;
}
if(ret==KErrNone)
ret = ActionEvent(ECallEventAnswerIncoming,KErrNone);
}
LOGVOICE3("<<CSimVoiceCall::AnswerIncomingCall 0x%08x, state %d exit",this,iState);
return ret;
}
TInt CSimVoiceCall::AnswerIncomingCallCancel(const TTsyReqHandle /*aTsyReqHandle*/)
/**
* Cancels a AnswerIncomingCall Request
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
LOGVOICE1(">>CSimVoiceCall::AnswerIncomingCallCancel");
if(iAnswerIncomingCall.iNotifyPending)
{
iAnswerIncomingCall.iNotifyPending=EFalse;
iLine->ResetAutoAnswerCallObject(this);
ReqCompleted(iAnswerIncomingCall.iNotifyHandle,KErrCancel);
}
LOGVOICE1("<<CSimVoiceCall::AnswerIncomingCallCancel");
return KErrNone;
}
TInt CSimVoiceCall::AnswerIncomingCallISVL(const TTsyReqHandle aTsyReqHandle,const TDesC8* aCallParams)
/**
* Register an EtelISV client's interest in answering the next incoming call.
* First register interest in incoming calls with the line, then, if a call
* is already ringing, start the answer procedure.
*
* @param aTsyReqHandle
* @param aCallParams the call parameters
* @return KErrNone
*/
{
LOGVOICE3(">>CSimVoiceCall::AnswerIncomingCallISV 0x%08x, state %d entry ",this,iState);
//see where the answer request has orignated from
if(aCallParams)
{
RCall::TCallParamsPckg* callparamsPckg=reinterpret_cast<RCall::TCallParamsPckg*>(const_cast<TDesC8*>(aCallParams));
RCall::TCallParams& callparams=(*callparamsPckg)();
if(callparams.ExtensionId() == RMobileCall::KETel3rdPartyCallParamsV1)
{
LOGVOICE1("<<CSimVoiceCall::AnswerIncomingCallISV request from Etel 3rd Party client.");
}
}
TInt ret=iLine->SetAutoAnswerCallObject(this);
if(ret!=KErrNone)
{
ReqCompleted(aTsyReqHandle,ret);
return KErrNone;
}
iAnswerIncomingCall.iNotifyPending=ETrue;
iAnswerIncomingCall.iNotifyHandle=aTsyReqHandle;
if(iState==RMobileCall::EStatusRinging)
ret = ActionEvent(ECallEventAnswerIncoming,KErrNone);
else if(iLine->iState==RMobileCall::EStatusRinging)
{
TRAPD(leaveValue, ret=ChangeStateL(RMobileCall::EStatusRinging,EFalse,EFalse));
if (leaveValue != KErrNone)
{
return leaveValue;
}
if (ret == KErrNone)
{
ret = ActionEvent(ECallEventAnswerIncoming,KErrNone);
}
}
LOGVOICE3("<<CSimVoiceCall::AnswerIncomingCallISV 0x%08x, state %d exit",this,iState);
return ret;
}
TInt CSimVoiceCall::AnswerIncomingCallISVCancel(const TTsyReqHandle /*aTsyReqHandle*/)
/**
* Cancels an EtelISV originated AnswerIncomingCall Request
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
LOGVOICE1(">>CSimVoiceCall::AnswerIncomingCallISVCancel");
if(iAnswerIncomingCall.iNotifyPending)
{
iAnswerIncomingCall.iNotifyPending=EFalse;
iLine->ResetAutoAnswerCallObject(this);
ReqCompleted(iAnswerIncomingCall.iNotifyHandle,KErrCancel);
}
LOGVOICE1("<<CSimVoiceCall::AnswerIncomingCallISVCancel");
return KErrNone;
}
TInt CSimVoiceCall::Connect(const TTsyReqHandle aTsyReqHandle,const TDesC8* /*aCallParams*/)
{
ReqCompleted(aTsyReqHandle,KErrNotSupported);
return KErrNone;
}
TInt CSimVoiceCall::ConnectCancel(const TTsyReqHandle aTsyReqHandle)
{
ReqCompleted(aTsyReqHandle,KErrNotSupported);
return KErrNone;
}
TInt CSimVoiceCall::HangUp(const TTsyReqHandle aTsyReqHandle)
/**
* Process a client's HangUp request.
*
* @param aTsyReqHandle
* @return KErrNone
*/
{
LOGVOICE1(">>CSimVoiceCall::HangUp");
iHangUpRequestHandle=aTsyReqHandle;
TInt ret=ActionEvent(ECallEventHangUp,KErrNone);
if(ret!=KErrNone)
ReqCompleted(aTsyReqHandle,ret);
LOGVOICE1("<<CSimVoiceCall::HangUp");
return KErrNone;
}
TInt CSimVoiceCall::HangUpCancel(const TTsyReqHandle /*aTsyReqHandle*/)
/**
* Cancels a HangUp Request
*
* @param aTsyReqHandle
* @return KErrNone if successfully cancelled
*/
{
LOGVOICE1(">>CSimVoiceCall::HangUpCancel");
switch(iState)
{
case RMobileCall::EStatusIdle:
SimPanic(EIllegalCancelRequest); // A DialCancel should never reach the TSY in this state.
break;
case RMobileCall::EStatusDisconnecting:
iTimer->DoCancel();
// Can ignore for cancel
TInt err;
err = KErrNone;
TRAP(err, (void)ChangeStateL(RMobileCall::EStatusConnected,EFalse,EFalse));
if (err != KErrNone)
{
ReqCompleted(iHangUpRequestHandle, err);
}
else
{
ReqCompleted(iHangUpRequestHandle, KErrCancel);
}
break;
default:
break;
}
LOGVOICE1("<<CSimVoiceCall::HangUpCancel");
return KErrNone;
}
TInt CSimVoiceCall::RelinquishOwnership()
{
return KErrNotSupported;
}
TInt CSimVoiceCall::GetBearerServiceInfo(const TTsyReqHandle aTsyReqHandle,RCall::TBearerService* /*aBearerService*/)
/**
* Retrieves the bearer service info
* Not supported for voice calls
*/
{
ReqCompleted(aTsyReqHandle,KErrNotSupported);
return KErrNone;
}
TInt CSimVoiceCall::GetCallParams(const TTsyReqHandle aTsyReqHandle, TDes8* /*aParams*/)
/**
* Retrives the call parameters
* Not supported for voice calls
*/
{
ReqCompleted(aTsyReqHandle,KErrNotSupported);
return KErrNone;
}
TInt CSimVoiceCall::LoanDataPort(const TTsyReqHandle,RCall::TCommPort*)
/**
* Loans the comm port.
* This is a data call specific request so not supported here.
*/
{
return KErrNotSupported;
}
/**
* Cancels the LoanDataPort request.
* This is a data call specific request so not supported here.
*/
TInt CSimVoiceCall::LoanDataPortCancel(const TTsyReqHandle)
{
return KErrNotSupported;
}
TInt CSimVoiceCall::RecoverDataPort(const TTsyReqHandle)
/**
* Recovers the comm port.
* This is a data call specific request so not supported here.
*/
{
return KErrNotSupported;
}
TInt CSimVoiceCall::Hold(const TTsyReqHandle aReqHandle)
/**
* Put the call on hold.
* @param aReqHandle The request handle associated with this call.
* @return TInt Standard error value.
*/
{
if(iState!=RMobileCall::EStatusConnected)
{
ReqCompleted(aReqHandle,KErrEtelCallNotActive);
return KErrNone;
}
iHoldResumeRequestHandle=aReqHandle;
TInt ret=ActionEvent(ECallEventHold,KErrNone);
return ret;
}
TInt CSimVoiceCall::HoldCancel(const TTsyReqHandle)
/**
* Cancel a Hold request. Since hold is completed synchronously, this function is empty.
*/
{
return KErrNone;
}
/**
* Cancel a NotifyConnectConfirmation request. It is possible to cancel
* a pending notification if the client did not issue a Dial request.
* @param aReqHandle The request handle associated with this call.
*/
TInt CSimVoiceCall::NotifyConnectConfirmationCancel(const TTsyReqHandle aReqHandle)
{
if (iNotifyConnectConfirmReqHandle==aReqHandle)
{
iNotifyConnectConfirmReqHandle = NULL;
}
ReqCompleted(aReqHandle,KErrCancel);
return KErrNone;
}
/**
* Resume a pending MO Connection.
* @param aReqHandle The request handle associated with this call.
* @param aConnectContinue Boolean to indicate whether to continue/terminate
* the call connection.
* @return TInt Standard error value.
*/
TInt CSimVoiceCall::ResumeConnect(const TTsyReqHandle aReqHandle, const TBool * aConnectContinue)
{
TInt ret=KErrNone;
if(iWaitForConnectConfirm)
{
// synchronous operation for either case
if (*aConnectContinue)
{
// okay to continue, initiate dialing, this will
// change the state
ret=ActionEvent(ECallEventConnectContinue,KErrNone);
}
else
{
// terminate the call...
// there must be a dial request previously issue,
// but we never send the dial request down to call-stack,
// and we did not invoke the dial timer, so it will
// never expire, so we complete the dial request here
ReqCompleted(iDialRequestHandle,KErrNone);
}
// complete the resume request
ReqCompleted(aReqHandle,ret);
}
else
{
// client did not issue dial request previously, this request
// is received in the wrong state, reject this request
ReqCompleted(aReqHandle,KErrAccessDenied);
}
return ret;
}
/**
* Cancel a ResumeConnect request. Since resume is completed synchronously, this function is empty.
*/
TInt CSimVoiceCall::ResumeConnectCancel(const TTsyReqHandle)
{
return KErrNone;
}
TInt CSimVoiceCall::Resume(const TTsyReqHandle aReqHandle)
/**
* Put the call on resume.
* @param aReqHandle The request handle associated with this call.
* @return TInt Standard error value.
*/
{
if(iState!=RMobileCall::EStatusHold)
{
ReqCompleted(aReqHandle,KErrAccessDenied);
return KErrNone;
}
iHoldResumeRequestHandle=aReqHandle;
TInt ret=ActionEvent(ECallEventResume,KErrNone);
return ret;
}
TInt CSimVoiceCall::ResumeCancel(const TTsyReqHandle)
/**
* Cancel a Resume request. Since resume is completed synchronously, this function is empty.
*/
{
return KErrNone;
}
TInt CSimVoiceCall::Swap(const TTsyReqHandle aReqHandle)
/**
* If the call is active, put it on hold. If its on hold, resume it.
* @param aReqHandle The request handle associated with this call.
* @return TInt Standard error value.
*/
{
TCallEvent event;
if(iState==RMobileCall::EStatusConnected)
{
event=ECallEventHold;
}
else if(iState==RMobileCall::EStatusHold)
{
event=ECallEventResume;
}
else
{
ReqCompleted(aReqHandle,KErrEtelCallNotActive);
return KErrNone;
}
iHoldResumeRequestHandle=aReqHandle;
TInt ret=ActionEvent(event,ECallEventSwap);
return ret;
}
TInt CSimVoiceCall::SwapCancel(const TTsyReqHandle)
/**
* Cancel a Swap request. Since swap is completed synchronously, this function is empty.
*/
{
return KErrNone;
}
TInt CSimVoiceCall::ActionEvent(TCallEvent aEvent,TInt aOtherArgument)
/**
* Entry point when an event has occured that may advance the state machine.
* The aEvent parameter describes the event.
*
* This function contains the main state machine for the voice call. The outer layer
* switches on the event type. Where appropriate, there are inner layer switches
* or conditional statements to handle the different responses that may be required to
* the same event occurring in different states.
*
* @param aEvent The Call event to handle
* @return value represents the error state caused by the attempted state machine jump.
*/
{
TInt ret=KErrNone;
__ASSERT_ALWAYS(iState!=RMobileCall::EStatusUnknown,SimPanic(ECallStatusUnknownIllegal));
__ASSERT_ALWAYS(aEvent!=ECallEventNtRasConnected,SimPanic(ECallEventIllegal));
LOGVOICE3(">>CSimVoiceCall::ActionEvent 0x%08x %d",this,iState);
switch(aEvent)
{
case ECallEventDial:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventDial]");
if(iState==RMobileCall::EStatusIdle)
{
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusDialling,EFalse,EFalse));
if(ret==KErrNone)
iTimer->Start(iDiallingPause,this);
}
else
return KErrEtelCallAlreadyActive;
break;
case ECallEventHangUp:
{
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventHangUp]");
switch(iState)
{
case RMobileCall::EStatusDialling:
case RMobileCall::EStatusRinging:
case RMobileCall::EStatusAnswering:
case RMobileCall::EStatusConnecting:
case RMobileCall::EStatusConnected:
case RMobileCall::EStatusHold:
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusDisconnecting,EFalse,EFalse));
if(ret==KErrNone)
iTimer->Start(iDisconnectingPause,this);
if(iSimDtmf)
{
iSimDtmf->CallClosureCallback();
iSimDtmf=NULL;
}
break;
default:
return KErrEtelCallNotActive;
}
}
break;
case ECallEventIncomingCall:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventIncomingCall]");
if(iState==RMobileCall::EStatusIdle)
{
if(iAnswerIncomingCall.iNotifyPending)
{
TRAP(ret, ret=ProcessAnswerIncomingCallL());
}
else
{
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusRinging,EFalse,EFalse));
if(ret!=KErrNone)
return ret;
}
}
else
return KErrEtelCallAlreadyActive;
break;
case ECallEventAnswerIncoming:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventAnswerIncoming]");
if(iState==RMobileCall::EStatusRinging)
{
TRAP(ret, ret=ProcessAnswerIncomingCallL());
if(ret!=KErrNone)
{
return ret;
}
}
else
SimPanic(EIllegalStateInconsistancy); // This is checked before calling ActionEvent
break;
case ECallEventRemoteHangup:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventRemoteHangup]");
if(iState==RMobileCall::EStatusConnected)
{
TRAP(ret, ret=ProcessRemoteHangupL());
if(ret!=KErrNone)
{
return ret;
}
}
else
SimPanic(EIllegalStateInconsistancy); // This is checked before calling ActionEvent
break;
case ECallEventTimeOut:
{
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventTimeOut]");
switch(iState)
{
case RMobileCall::EStatusDialling:
LOGVOICE1(">>CSimVoiceCall::State = [EStatusDialling]");
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusConnecting,EFalse,EFalse));
if(ret==KErrNone)
iTimer->Start(iConnectingPause,this);
return ret;
case RMobileCall::EStatusConnecting:
LOGVOICE1(">>CSimVoiceCall::State = [EStatusConnecting]");
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusConnected,EFalse,EFalse));
UpdateRemotePartyInfoDirection(RMobileCall::EStatusConnecting);
ReqCompleted(iDialRequestHandle,ret);
return ret;
case RMobileCall::EStatusDisconnecting:
LOGVOICE1(">>CSimVoiceCall::State = [EStatusDisconnecting]");
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusIdle,EFalse,EFalse));
ReqCompleted(iHangUpRequestHandle,ret);
return ret;
case RMobileCall::EStatusAnswering:
LOGVOICE1(">>CSimVoiceCall::State = [EStatusAnswering]");
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusConnected,EFalse,EFalse));
UpdateRemotePartyInfoDirection(RMobileCall::EStatusAnswering);
ReqCompleted(iAnswerIncomingCall.iNotifyHandle,ret);
return ret;
default:
break;
}
}
break;
case ECallEventHold:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventHold]");
__ASSERT_ALWAYS(iState==RMobileCall::EStatusConnected,SimPanic(EIllegalStateInconsistancy));
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusHold,aOtherArgument == ECallEventSwap,EFalse));
if (ret == KErrNone)
ReqCompleted(iHoldResumeRequestHandle,ret);
break;
case ECallEventResume:
LOGVOICE1(">>CSimVoiceCall::ActionEvent = [ECallEventResume]");
__ASSERT_ALWAYS(iState==RMobileCall::EStatusHold,SimPanic(EIllegalStateInconsistancy));
TRAP(ret, ret=ChangeStateL(RMobileCall::EStatusConnected,aOtherArgument == ECallEventSwap,EFalse));
if (ret == KErrNone)
ReqCompleted(iHoldResumeRequestHandle,ret);
break;
case ECallEventConnectContinue:
SimPanic(EIllegalVoiceCallEvent); // All other events not legal for voice call.
break;
default:
SimPanic(EIllegalVoiceCallEvent); // All other events not legal for voice call.
break;
}
return ret;
}
void CSimVoiceCall::TimerCallBack(TInt /*aId*/)
/**
* Timer callback function. When the timer goes off, it will call back into this
* function for further processing.
*/
{
LOGVOICE1(">>CSimVoiceCall::TimerCallBack");
TInt ret=ActionEvent(ECallEventTimeOut,KErrNone);
__ASSERT_ALWAYS(ret==KErrNone,SimPanic(ETimeOutEventActionFailed));
LOGVOICE1("<<CSimVoiceCall::TimerCallBack");
}
TInt CSimVoiceCall::ProcessAnswerIncomingCallL()
/**
* Answers an Incoming Call.
* First the call state must be changed to "answering", then the flag indicating
* that an answer incoming call request is no longer pending. Finally, a new
* call object must be assigned to receive the details of the next incoming call.
*/
{
LOGVOICE3(">>CSimVoiceCall::ProcessAnswerIncomingCall %d",iState,this);
TInt ret=ChangeStateL(RMobileCall::EStatusAnswering,EFalse,EFalse);
if(ret!=KErrNone)
return ret;
iTimer->Start(iAnswerIncomingPause,this);
iAnswerIncomingCall.iNotifyPending=EFalse;
iLine->ResetAutoAnswerCallObject(this);
LOGVOICE1("<<CSimVoiceCall::ProcessAnswerIncomingCall");
return ret;
}
TInt CSimVoiceCall::ProcessRemoteHangupL()
/**
* Hangs up a call remotely.
* First the call state must be changed to "disconnecting", then the flag indicating
* that a remote hangup request is no longer pending. Finally, a new
* call object must be assigned to receive the next remote hangup request.
*/
{
LOGVOICE3(">>CSimVoiceCall::ProcessRemoteHangupL %d",iState,this);
TInt ret=ChangeStateL(RMobileCall::EStatusDisconnecting,EFalse,EFalse);
if(ret!=KErrNone)
return ret;
iTimer->Start(iRemoteHangupPause,this);
iLine->ResetRemoteHangupCallObject(this);
LOGVOICE1("<<CSimVoiceCall::ProcessRemoteHangupL");
return ret;
}
void CSimVoiceCall::SetDtmfSession(CSimDtmf* aSimDtmf)
/**
* Set the DTMF session pointer.
*/
{
__ASSERT_ALWAYS(iState==RMobileCall::EStatusConnected,SimPanic(EIllegalDtmfReq));
iSimDtmf=aSimDtmf;
}
CSimDtmf* CSimVoiceCall::GetDtmfSession()
/**
* Retrieve the DTMF session pointer.
*/
{
return iSimDtmf;
}
TUint CSimVoiceCall::Caps()
/**
* Return the current capabilities of this call.
* @return TUint Current call capabilities.
*/
{
TUint caps=RCall::KCapsVoice;
if(iState!=RMobileCall::EStatusIdle)
{
if(iState==RMobileCall::EStatusConnected)
{
caps |= RCall::KCapsHangUp;
// If there is only a single call and its state is connected then the hold capability
// should be set.
// If there is more than one call then only the swap capability should be set as hold
// and resume apply only to single calls.
TInt i=0;
TBool singleCall = ETrue;
TInt count= iLine->iCalls->Count();
while(singleCall && i<count)
{
if(iLine->iCalls->At(i) != this && (iLine->iCalls->At(i)->iState == RMobileCall::EStatusConnected || iLine->iCalls->At(i)->iState == RMobileCall::EStatusHold))
singleCall = EFalse;
i++;
}
if (singleCall)
caps |= RMobileCall::KCapsHold;
// Swap capability is applicable to single and multiple call
// scenarios
caps |= RMobileCall::KCapsSwap;
}
else if(iState==RMobileCall::EStatusHold)
{
caps |= RCall::KCapsHangUp;
TBool otherConnected = EFalse;
TInt count= iLine->iCalls->Count();
TInt i=0;
while (!otherConnected && i<count)
{
if(iLine->iCalls->At(i) != this && (iLine->iCalls->At(i)->iState == RMobileCall::EStatusConnected || iLine->iCalls->At(i)->iState == RMobileCall::EStatusHold))
otherConnected = ETrue;
i++;
}
if(otherConnected)
{
caps |= RMobileCall::KCapsSwap;
}
else
{
caps |= RMobileCall::KCapsResume;
caps |= RMobileCall::KCapsSwap;
}
}
else if (iState==RMobileCall::EStatusRinging)
{
if((iLine->IsAnswerCallObjectSpare()))
caps |= RCall::KCapsAnswer;
}
}
else
{
if(iLine->iState==RMobileCall::EStatusIdle || iLine->iState==RMobileCall::EStatusHold)
{
caps |= RCall::KCapsDial;
if((iLine->IsAnswerCallObjectSpare()))
caps |= RCall::KCapsAnswer;
}
else if (iLine->iState==RMobileCall::EStatusRinging)
{
if((iLine->IsAnswerCallObjectSpare()))
caps |= RCall::KCapsAnswer;
}
}
return caps;
}
void CSimVoiceCall::UpdateRemotePartyInfoDirection(RMobileCall::TMobileCallStatus aPreviousStatus)
/**
Update the direction of the call for the remote party info member
*/
{
if(iNotifyRemotePartyInfoTimer && iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iRemoteIdStatus != RMobileCall::ERemoteIdentityUnknown)
{
if(aPreviousStatus == RMobileCall::EStatusAnswering)
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iDirection = RMobileCall::EMobileTerminated;
else
iNotifyRemotePartyInfoTimer->iRemotePartyInfoV1.iDirection = RMobileCall::EMobileOriginated;
}
}