diff -r 000000000000 -r 3553901f7fa8 telephonyserverplugins/simtsy/src/CSimLine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telephonyserverplugins/simtsy/src/CSimLine.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,927 @@ + +// 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 for the functionality common to all +// the specific line classes, such as CSimVoiceLine and CSimDataline. +// +// + +/** + @file +*/ + +#include +#include "CSimLine.h" +#include "CSimPhone.h" +#include "CSimCall.h" +#include "Simlog.h" +#include "CSimPubSub.h" + +void CSimLine::CloseLine(TAny* aObj) +/** +* Utility func for cleanup stack +* +* @param aObj a pointer to the CObject to close +*/ + { + ((CObject*)aObj)->Close(); + } + +CSimLine::CSimLine(CSimPhone* aPhone) + :iPhone(aPhone), iState(RMobileCall::EStatusIdle), + iHookState(ConvertStateToHook(iState)) + { + iNameOfLastCallAdded.Zero(); + iNotifyStatusChange.iNotifyPending = EFalse; + iNotifyMobileStatusChange.iNotifyPending = EFalse; + } + +void CSimLine::ConstructL(const TName& aName) +/** +* Second phase of two phase constructor. +* Creates the System Agent object for incoming call notification and other notifications. +* +* @param aName name of the Line to be created. +*/ + { + LOGLINE2(">>CSimLine::ConstructL [aLineName=%S]", &aName); + iLineName = aName; + iSimPubSubIC = CSimPubSub::NewL(this,iICProperty); + iSimPubSubIC->Start(); + + iSimPubSubRH = CSimPubSub::NewL(this,iRHProperty); + iSimPubSubRH->Start(); + + iNotifyIncomingCallPause=this->CfgFile()->ItemValue(KNotifyIncomingCallPauseDuration,KDefaultNotifyIncomingCallPauseDuration); + + iTimer = CSimTimer::NewL(iPhone); + LOGLINE1("<Cancel(); + delete iTimer; + } + + +void CSimLine::UpdatePhoneNotifiers(CSimCall* aCall,TUint /*aStatus*/) + { + TInt count= iCalls->Count(); + TInt i=0; + while(iAt(i) != aCall && (iCalls->At(i)->iState == RMobileCall::EStatusHold || iCalls->At(i)->iState == RMobileCall::EStatusConnected)) + iCalls->At(i)->UpdateNotifiers(); + i++; + } + } + +CTelObject::TReqMode CSimLine::ReqModeL(const TInt aIpc) +/** +* This function returns the Request Mode for the request with the passed IPC value. +* The ETel Server provides a function for returning the standard request modes for +* the Core API requests. +* Multimode API requests mode are handled here. +* +* @param aIpc the IPc number representing the client request +* @return CTelObject::TReqMode the request mode to be used for this IPc number +* @leave Leaves if the IPc number is not found +*/ + { + // ReqModeL is called from the server's CTelObject::ReqAnalyserL + // in order to check the type of request it has + + CTelObject::TReqMode reqMode=0; + switch (aIpc) + { +// +// No Flow Control NOR Multiple Completion +// + case EMobileLineGetMobileLineStatus: + break; + +// +// Multiple Completion Services with Immediate Server Repost +// (Usually Notifications) +// + case EMobileLineNotifyMobileLineStatusChange: + reqMode=KReqModeMultipleCompletionEnabled | KReqModeRePostImmediately; + break; +// +// Must be a core API request so get request mode from Etel +// + default: + reqMode=CLineBase::ReqModeL(aIpc); + break; + } + + return reqMode; + } + + +TInt CSimLine::NumberOfSlotsL(const TInt aIpc) +/** +* NumberOfSlotsL is called by the server when it is registering a new notification +* It enables the TSY to tell the server how many buffer slots to allocate for +* "repost immediately" notifications that may trigger before clients collect them +* +* @param aIpc the IPc number representing the client request +* @return TInt the number of slots required +* @leave Leaves if the IPc number is not found +*/ + { + switch (aIpc) + { + case EMobileLineNotifyMobileLineStatusChange: + LOGLINE1("CLineMobile: Registered with the default number of slots"); + return KDefaultNumberOfSlots; + + default: + LOGLINE1("CSimLine::NumberOfSlotsL: No match for IPC, defering to base function"); + break; + } + return CLineBase::NumberOfSlotsL(aIpc); + } + + +TInt CSimLine::RegisterNotification(const TInt /*aIpc*/) +/** +* The ETel Server calls this function when the first client makes a notification request. +* If supported by the underlying protocol controlling the signalling stack, +* this can be used to start requesting updates for the relevant service. +*/ + { + return KErrNone; + } + +TInt CSimLine::DeregisterNotification(const TInt /*aIpc*/) +/** +* The ETel Server calls this function when the last client that had previously +* made a notification request closes its ETel Server handle. If supported by +* the underlying protocol controlling the signalling stack, this can be used +* to stop requesting updates for the relevant service. +*/ + { + return KErrNone; + } + +void CSimLine::Init() +/** +* This function can be used to perform any necessary synchronous initialisation. +*/ + {} + +TInt CSimLine::NotifyIncomingCall(const TTsyReqHandle aTsyReqHandle, TName* aName) +/** +* Register a client's interest in an incoming call notification. +* Core ETel API Request. +* +* @param aTsyReqHandle Tsy Request handle for the client request +* @param aName pointer to the name of the call +* @return KErrNone +*/ + { + LOGLINE1(">>CSimLine::NotifyIncomingCall"); + iNotifyIncomingCall.iNotifyPending=ETrue; + iNotifyIncomingCall.iNotifyHandle=aTsyReqHandle; + iNotifyIncomingCall.iNotifyData=(TAny*)aName; + LOGLINE1("<>CSimLine::NotifyIncomingCallCancel"); + if(iNotifyIncomingCall.iNotifyPending) + { + iTimer->Cancel(); + iNotifyIncomingCall.iNotifyPending=EFalse; + ReqCompleted(iNotifyIncomingCall.iNotifyHandle, KErrCancel); + } + LOGLINE1("<>CSimLine::NotifyHookChange"); + iNotifyHookChange.iNotifyPending=ETrue; + iNotifyHookChange.iNotifyHandle=aTsyReqHandle; + iNotifyHookChange.iNotifyData=(TAny*)aHookStatus; + LOGLINE1("<>CSimLine::NotifyHookChangeCancel"); + if(iNotifyHookChange.iNotifyPending) + { + iNotifyHookChange.iNotifyPending=EFalse; + ReqCompleted(iNotifyHookChange.iNotifyHandle,KErrCancel); + } + LOGLINE1("<>CSimLine::NotifyMobileLineStatusChange"); + iNotifyMobileStatusChange.iNotifyPending=ETrue; + iNotifyMobileStatusChange.iNotifyHandle=aTsyReqHandle; + iNotifyMobileStatusChange.iNotifyData=aStatus; + LOGLINE1("<>CSimLine::NotifyMobileLineStatusChangeCancel"); + if(iNotifyMobileStatusChange.iNotifyPending) + { + iNotifyMobileStatusChange.iNotifyPending=EFalse; + ReqCompleted(iNotifyMobileStatusChange.iNotifyHandle,KErrCancel); + } + LOGLINE1("<>CSimLine::NotifyStatusChange"); + iNotifyStatusChange.iNotifyPending=ETrue; + iNotifyStatusChange.iNotifyHandle=aTsyReqHandle; + iNotifyStatusChange.iNotifyData=aStatus; + LOGLINE1("<>CSimLine::NotifyStatusChangeCancel"); + if(iNotifyStatusChange.iNotifyPending) + { + iNotifyStatusChange.iNotifyPending=EFalse; + ReqCompleted(iNotifyStatusChange.iNotifyHandle,KErrCancel); + } + LOGLINE1("<>CSimLine::NotifyCallAdded"); + iNotifyCallAdded.iNotifyPending=ETrue; + iNotifyCallAdded.iNotifyHandle=aTsyReqHandle; + iNotifyCallAdded.iNotifyData=(TAny*)aName; + LOGLINE1("<>CSimLine::NotifyCallAddedCancel"); + if(iNotifyCallAdded.iNotifyPending) + { + iNotifyCallAdded.iNotifyPending=EFalse; + ReqCompleted(iNotifyCallAdded.iNotifyHandle,KErrCancel); + } + LOGLINE1("<>CSimLine::HandleNewCallAddedNotification"); + iNameOfLastCallAdded.Copy(aNewName); + if(iNotifyCallAdded.iNotifyPending) + { + iNotifyCallAdded.iNotifyPending=EFalse; + ((TName*)iNotifyCallAdded.iNotifyData)->Copy(aNewName); + ReqCompleted(iNotifyCallAdded.iNotifyHandle,KErrNone); + } + LOGLINE1("<iFlags=iCaps; + ReqCompleted(aTsyReqHandle,KErrNone); + return KErrNone; + } + +TInt CSimLine::NotifyCapsChange(const TTsyReqHandle aTsyReqHandle, RLine::TCaps* aCaps) +/** +* Register a client's interest in being notified when the line caps change. +* Core ETel API Request. +* +* @param aTsyReqHandle The TSY handle associated with this request. +* @param aCaps The capability structure that will be populated with the new capability +* information. +* @return TInt Standard error code. +*/ + { + __ASSERT_ALWAYS(!iNotifyCapsChange.iNotifyPending,SimPanic(ENotificationReqAlreadyOutstanding)); + iNotifyCapsChange.iNotifyPending=ETrue; + iNotifyCapsChange.iNotifyHandle=aTsyReqHandle; + iNotifyCapsChange.iNotifyData=aCaps; + return KErrNone; + } + +TInt CSimLine::NotifyCapsChangeCancel(const TTsyReqHandle /*aTsyReqHandle*/) +/** +* Cancel a client's interest in being notified when the call capabilities change. +* Core ETel API Request. +*/ + { + if(iNotifyCapsChange.iNotifyPending) + { + iNotifyCapsChange.iNotifyPending=EFalse; + ReqCompleted(iNotifyCapsChange.iNotifyHandle,KErrCancel); + } + return KErrNone; + } + +TInt CSimLine::GetInfo(const TTsyReqHandle aTsyReqHandle, RMobileLine::TLineInfo* aLineInfo) +/** +* Retrieve the Line Information +* Core ETel API Request. +* +* @param aTsyReqHandle +* @param aLineInfo pointer to the line information to be returned to client +* @return KErrNone +*/ + { + LOGLINE1(">>CSimLine::GetInfo"); + aLineInfo->iHookStatus=iHookState; + aLineInfo->iStatus=(RCall::TStatus)iState; + aLineInfo->iNameOfLastCallAdded.Copy(iNameOfLastCallAdded); + if(iAnswerNextIncomingCall) + aLineInfo->iNameOfCallForAnswering.Copy(iAnswerNextIncomingCall->iName); + else + aLineInfo->iNameOfCallForAnswering.Zero(); + + ReqCompleted(aTsyReqHandle,KErrNone); + LOGLINE1("<>CSimLine::GetStatus"); + *aLineStatus=GetCoreLineStatus(); + ReqCompleted(aTsyReqHandle,KErrNone); + LOGLINE1("<>CSimLine::GetMobileLineStatus"); + *aLineStatus=iState; + ReqCompleted(aTsyReqHandle,KErrNone); + LOGLINE1("<>CSimLine::GetHookStatus"); + *aHookStatus=iHookState; + ReqCompleted(aTsyReqHandle,KErrNone); + LOGLINE1("<>CSimLine::CfgFile"); + return iPhone->CfgFile(); + } + +TInt CSimLine::ChangeStateL(RMobileCall::TMobileCallStatus aNewState,TBool aSwap,CSimCall* aOriginatingCall) +/** +* Validate and change the Line State +* +* @param aNewState the new state to change to +* @param aSwap indicates whether change comes from swap operation on the call +* @param aOriginatingCall original call that change comes from +* @return Error indication if change of state is successful or not +*/ + { + __ASSERT_ALWAYS(aNewState!=RMobileCall::EStatusUnknown,SimPanic(ECallStatusUnknownIllegal)); + __ASSERT_ALWAYS(iState!=RMobileCall::EStatusUnknown,SimPanic(ECallStatusUnknownIllegal)); + CSimCall* swappedCall=NULL; + TInt i=0; + TInt count=iCalls->Count(); + for(i=0;i>CSimLine::ChangeState0 0x%08x %d",iCalls->At(i),iCalls->At(i)->iState); + } + + TInt ret=KErrGeneral; + const TStateChangeValidity* stateChangePnt=KLineStateChangeValidity; + while(stateChangePnt->iOldState!=KStateTableTerminator) + { + if((stateChangePnt->iOldState==iState) && + ((stateChangePnt->iNewState==aNewState) || aNewState==RMobileCall::EStatusIdle)) + { + ret=stateChangePnt->iError; + break; + } + stateChangePnt++; + } + + //coverity[cannot_single] + if(ret!=KErrNone && !(aSwap && iState==RMobileCall::EStatusConnected && aNewState==RMobileCall::EStatusConnected)) + return ret; + + LOGLINE2(">>CSimLine::ChangeState 0x%08x",aOriginatingCall); + if(aNewState==RMobileCall::EStatusIdle) + { + if(iState==RMobileCall::EStatusIdle) + return KErrAlreadyExists; + else if(iState==RMobileCall::EStatusDisconnecting || iState==RMobileCall::EStatusDialling) + { + TInt i=0; + TInt count=iCalls->Count(); + for(i=0;i>CSimLine::ChangeState1 0x%08x %d",iCalls->At(i),iCalls->At(i)->iState); + + if(aOriginatingCall!=iCalls->At(i) && iCalls->At(i)->iState != RMobileCall::EStatusIdle) + { + aNewState=iCalls->At(i)->iState; + break; + } + } + } + } + else if(aNewState==RMobileCall::EStatusHold) + { + if((iState == RMobileCall::EStatusRinging) && + (aNewState == RMobileCall::EStatusHold)) + { + if(aOriginatingCall->iState != RMobileCall::EStatusConnected) + return KErrGeneral; + aNewState=RMobileCall::EStatusRinging; + } + TInt i=0; + TInt count=iCalls->Count(); + for(i=0;i>CSimLine::ChangeState1 0x%08x %d",iCalls->At(i),iCalls->At(i)->iState); + if(iCalls->At(i) != aOriginatingCall && iCalls->At(i)->iState == RMobileCall::EStatusHold) + { + if(aSwap) + { + swappedCall=iCalls->At(i); + swappedCall->iState=RMobileCall::EStatusConnected; + aNewState=RMobileCall::EStatusConnected; + } + else + return KErrAlreadyExists; + } + } + } + else if(aNewState==RMobileCall::EStatusConnected) + { + TInt i=0; + TInt count=iCalls->Count(); + for(i=0;i>CSimLine::ChangeState1 0x%08x %d",iCalls->At(i),iCalls->At(i)->iState); + if(iCalls->At(i) != aOriginatingCall) + { + if(iCalls->At(i)->iState == RMobileCall::EStatusConnected) + { + //coverity[dead_error_condition] + if(aSwap) + { + swappedCall=iCalls->At(i); + swappedCall->iState=RMobileCall::EStatusHold; + aNewState=RMobileCall::EStatusConnected; + break; + } + else + //coverity[dead_error_line] + return KErrAlreadyExists; + } + } + } + } + + // Actually change the state. + LOGLINE3(">>CSimLine::ChangeState [oldState=%d] [newState=%d]", iState, aNewState); + + if(iState != aNewState) + { + iState=aNewState; + + // Check for a pending line state notification (core) + if(iNotifyStatusChange.iNotifyPending) + { + iNotifyStatusChange.iNotifyPending=EFalse; + *(RCall::TStatus*)iNotifyStatusChange.iNotifyData=GetCoreLineStatus(); + ReqCompleted(iNotifyStatusChange.iNotifyHandle,KErrNone); + } + + // Check for a pending line state notification (multimode) + if(iNotifyMobileStatusChange.iNotifyPending) + { + iNotifyMobileStatusChange.iNotifyPending=EFalse; + *(RMobileCall::TMobileCallStatus*)iNotifyMobileStatusChange.iNotifyData=iState; + ReqCompleted(iNotifyMobileStatusChange.iNotifyHandle,KErrNone); + } + + // Check for a pending hook state notification. + if(iHookState!=ConvertStateToHook(iState)) + { + iHookState=ConvertStateToHook(iState); + if(iNotifyHookChange.iNotifyPending) + { + iNotifyHookChange.iNotifyPending=EFalse; + *(RCall::THookStatus*)iNotifyHookChange.iNotifyData=iHookState; + ReqCompleted(iNotifyHookChange.iNotifyHandle,KErrNone); + } + } + } + // Check for a possible change in capabilities. + TUint caps=Caps(); + if(iCaps!=caps) + { + iCaps=caps; + if(iNotifyCapsChange.iNotifyPending) + { + iNotifyCapsChange.iNotifyPending=EFalse; + ((RLine::TCaps*)iNotifyCapsChange.iNotifyData)->iFlags=iCaps; + ReqCompleted(iNotifyCapsChange.iNotifyHandle,KErrNone); + } + } + + if(aSwap && swappedCall) + swappedCall->UpdateNotifiers(); + // Request that the phone change its state, to ensure its in step. + if((ret = iPhone->ValidateChangeState(this,aNewState)) != KErrNone) + return ret; + + for(i=0;iCount();i++) + { + LOGLINE3(">>CSimLine::ChangeState5 0x%08x %d",iCalls->At(i),iCalls->At(i)->iState); + } + + LOGLINE1("<>CSimLine::SimPSEvent"); + if(aProperty==iICProperty) // check for this here still, start the timer for amount specified in config file. + ProcessIncomingCallEvent(); + if(aProperty==iRHProperty) + ProcessRemoteHangupEvent(); + LOGLINE1("<>CSimLine::SetAnswerCallObject"); + if(iAnswerIncomingCallReqPending) // Is there a request already outstanding? + return KErrEtelAnswerAlreadyOutstanding; + + iAnswerIncomingCallReqPending=ETrue; + if (iAnswerNextIncomingCall!=aCallObject) + { + if (iSpareCall != iAnswerNextIncomingCall) + { + iAnswerNextIncomingCall->Close(); + } + iAnswerNextIncomingCall=aCallObject; // Set the "answer next call" object + iAnswerNextIncomingCall->Open(); + } + LOGLINE1("<>CSimLine::ResetAnswerCallObject"); + __ASSERT_ALWAYS(iAnswerNextIncomingCall==aCallObject,SimPanic(EIllegalAnswerNextIncomingCallInconsistancy)); + iAnswerNextIncomingCall->Close(); + iAnswerNextIncomingCall=iSpareCall; + iAnswerIncomingCallReqPending=EFalse; + } + +void CSimLine::ProcessIncomingCallEvent() +/** +* Process an incoming call event. +*/ + { + LOGLINE1(">>CSimLine::ProcessIncomingCallEvent "); + + if (iSpareCall == iAnswerNextIncomingCall) + { + TName callName; + TRAPD(err,iAnswerNextIncomingCall=CreateNewCallL(callName,ECallTypeNormalCall)); + if (err!=KErrNone) + { + SimPanic(EOjectNotConstructed); + } + } + + // Delay sending the notification of an incoming call if + // one has been set. + iTimer->Start(iNotifyIncomingCallPause, this); + + __ASSERT_ALWAYS(iAnswerNextIncomingCall->ActionEvent(CSimCall::ECallEventIncomingCall,KErrNone) == KErrNone, SimPanic(EGeneral)); + LOGLINE1("<>CSimLine::SetRemoteHangupCallObject"); + if(iRemoteHangupCallReqPending) // Is there a request already outstanding? + return KErrEtelInitialisationFailure; + + iRemoteHangupCallReqPending=ETrue; + if (iRemoteHangupCall!=aCallObject) + { + iRemoteHangupCall=aCallObject; // Set the "remote hang up" object + } + LOGLINE1("<>CSimLine::ResetRemoteHangupCallObject"); + __ASSERT_ALWAYS(iRemoteHangupCall==aCallObject,SimPanic(EIllegalAnswerRemoteHangupCallInconsistency)); + iRemoteHangupCallReqPending=EFalse; + } + +void CSimLine::ProcessRemoteHangupEvent() +/** +* Process a remote hangup event. +*/ + { + LOGLINE1(">>CSimLine::ProcessRemoteHangupEvent"); + + if (iRemoteHangupCall == NULL) + { + TInt err = FindActiveCall(iRemoteHangupCall); + if(err == KErrNone) + { + err = iRemoteHangupCall->ActionEvent(CSimCall::ECallEventRemoteHangup,KErrNone); + if(err != KErrNone) + { + LOGLINE2("ERROR: CSimLine::ProcessRemoteHangupEvent returned: %d", err); + } + } + else + { + LOGLINE1("No outstanding call to hang up remotely"); + } + } + LOGLINE1("<Count();i++) + { + if(iCalls->At(i)==aCall) + { + iCalls->Delete(i); + return; + } + } + SimPanic(EIllegalCallNotRegisteredWithLine); + } + +TBool CSimLine::IsAnswerCallObjectSpare() +/** + * Is the "answer" call object actually the spare object. + */ + { + return (iSpareCall==iAnswerNextIncomingCall); + } + +/** +Called when iTimer has delayed the sending of the notification of the incoming call. +*/ +void CSimLine::TimerCallBack(TInt /*aId*/) + { + ProcessNotifyIncomingCallEvent(); + } + +/** +Send notification of an incoming call. +*/ +void CSimLine::ProcessNotifyIncomingCallEvent() + { + LOGLINE1(">>CSimLine::ProcessNotifyIncomingCallEvent"); + if(iNotifyIncomingCall.iNotifyPending) + { + iNotifyIncomingCall.iNotifyPending=EFalse; + ((TName*)iNotifyIncomingCall.iNotifyData)->Copy(iAnswerNextIncomingCall->iName); + ReqCompleted(iNotifyIncomingCall.iNotifyHandle,KErrNone); + } + LOGLINE1("<