// Copyright (c) 2000-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:
//
// User includes
#include "watcherlog.h"
#include "indicatorwatcher.h"
// System includes
#include <sacls.h>
#include <commsdat.h>
//
// ------> Global exports
//
//
// ------> CIndicatorWatcher (source)
//
CIndicatorWatcher::CIndicatorWatcher()
: CPhoneWatcher()
{
}
CIndicatorWatcher* CIndicatorWatcher::NewL(TAny* /*aWatcherParams*/)
{
CIndicatorWatcher* self= new (ELeave) CIndicatorWatcher();
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
void CIndicatorWatcher::ConstructL()
{
CWatcherBase::ConstructL();
iCallStateWatcher = new (ELeave) CCallStateWatcher(Phone());
/* Attach to properties for minimum access time */
User::LeaveIfError(iCurrentCallProperty.Attach(KUidSystemCategory, KUidCurrentCall.iUid));
User::LeaveIfError(iChargerStatusProperty.Attach(KUidSystemCategory, KUidChargerStatus.iUid));
User::LeaveIfError(iNetworkStatusProperty.Attach(KUidSystemCategory, KUidNetworkStatus.iUid));
}
CIndicatorWatcher::~CIndicatorWatcher()
{
Cancel();
delete iCallStateWatcher;
iCurrentCallProperty.Close();
iChargerStatusProperty.Close();
iNetworkStatusProperty.Close();
}
//
//
//
void CIndicatorWatcher::HandlePhoneStateEventL(TInt aCompletionCode)
{
switch(IndicatorState())
{
case EIndicatorNotYetInitialised:
case EIndicatorRequestInitialIndicator:
LOGINDICATOR1("IndicatorWatcher : Requesting initial indicator values");
Phone().GetIndicator(iStatus, iIndicatorInfo);
IndicatorState() = EIndicatorWaitingForInitialIndicator;
SetActive();
break;
case EIndicatorWaitingForInitialIndicator:
IndicatorState() = EIndicatorIssuingIndicatorChangeNotification;
HandleIndicatorUpdateL(aCompletionCode);
break;
case EIndicatorIssuingIndicatorChangeNotification:
HandleIndicatorUpdateL(aCompletionCode);
break;
default:
__ASSERT_DEBUG(0, IndicatorPanic(EUnexpectedState));
}
}
void CIndicatorWatcher::HandleCancel()
{
if (Phone().SubSessionHandle() == KNullHandle)
return;
if (IndicatorState() == EIndicatorWaitingForInitialIndicator)
Phone().CancelAsyncRequest(EMobilePhoneGetIndicator);
else if (IndicatorState() == EIndicatorIssuingIndicatorChangeNotification)
{
Phone().CancelAsyncRequest(EMobilePhoneNotifyIndicatorChange);
if (iCallStateWatcher)
iCallStateWatcher->Cancel();
}
}
void CIndicatorWatcher::ReleasePhoneResources()
//
// Called by the phone watcher base class. Release any telephony related
// resources and reset and state.
//
{
// This is only called within RunL and therefore we can't be active
__ASSERT_DEBUG(!IsActive(), IndicatorPanic(EUnexpectedActiveState));
// Reset state
iState = EIndicatorNotYetInitialised;
}
//
//
//
void CIndicatorWatcher::HandleIndicatorUpdateL(TInt aCompletionCode)
{
#ifdef _DEBUG
LOGINDICATOR2("IndicatorWatcher : Handling phone state change with request result (%d)", aCompletionCode);
#else
(void) aCompletionCode;
#endif
#ifdef WATCHER_TESTING
// TESTING:
{
// Get a random number which controls what state we update...
TInt index;
User::LeaveIfError(RProperty::Get(KUidSystemCategory,KUidTestProp_CallStateChange.iUid, index));
switch(index)
{
default:
case 0: // CALL STATE
{
TSACurrentCall state = static_cast<TSACurrentCall>(WHelpers::Rand(ESACallNone, ESACallAlternating, TheSeed));
User::LeaveIfError(iCurrentCallProperty.Set(state));
break;
}
case 1: // BATTERY CHARGER
{
TSAChargerStatus state = static_cast<TSAChargerStatus>(WHelpers::Rand(ESAChargerConnected, ESAChargerNotCharging, TheSeed));
User::LeaveIfError(iChargerStatusProperty.Set(state));
break;
}
case 2: // NETWORK AVAILABILITY
{
TSANetworkStatus state = static_cast<TSANetworkStatus>(WHelpers::Rand(ESANetworkAvailable, ESANetworkUnAvailable, TheSeed));
User::LeaveIfError(iNetworkStatusProperty.Set(state));
break;
}
}
SuspendFor(10); // seconds
return;
}
#else
if (aCompletionCode < KErrNone)
{
// Indicate we don't know what the indicator status is
User::LeaveIfError(iCurrentCallProperty.Set(KErrUnknown));
User::LeaveIfError(iChargerStatusProperty.Set(KErrUnknown));
User::LeaveIfError(iNetworkStatusProperty.Set(KErrUnknown));
if (aCompletionCode == KErrNotSupported)
{
// If the TSY returns 'Not supported' then it isn't
// worth re-sending the request, so give up gracefully.
SetDisabled(_L("IndicatorNotifier : TSY returned not supported (%d)"), aCompletionCode);
}
else if (aCompletionCode == KErrCancel)
{
// Indicator watcher was cancelled
SetDisabled(_L("IndicatorNotifier : TSY has cancelled request (%d)"), aCompletionCode);
}
else if (aCompletionCode == KWatcherBaseModemNotDetected)
{
// We should release all telephony related resources until the
// phone is available again.
Cancel();
Reset();
// The modem / phone cannot be found. Wait until it becomes available again...
WaitForPhoneToPowerUpL();
}
else if (ErrorCountIncrement() >= KErrorRetryCount)
{
// We've already tried as many times as possible. Shut ourselves down forever.
// This watcher will be restarted when the machine is rebooted.
SetDisabled(_L("IndicatorNotifier : Max retries reached or exceeded. Shutting down until reboot."), 0);
}
else
SuspendFor(KErrorRetryPausePeriod);
}
else
{
LOGINDICATOR1("IndicatorWatcher : Processing successful indicator event");
// Update charger status if there has been a change
{
TInt chargerState;
User::LeaveIfError(iChargerStatusProperty.Get(chargerState));
TInt newChargerState=KErrUnknown;
if (iIndicatorInfo & RMobilePhone::KIndChargerConnected)
newChargerState=ESAChargerConnected;
else
newChargerState=ESAChargerDisconnected;
if (newChargerState!=chargerState)
{
LOGINDICATOR2("IndicatorWatcher : New Charger State %d", newChargerState);
User::LeaveIfError(iChargerStatusProperty.Set(newChargerState));
}
}
// Update network available status if there has been a change
{
TInt networkState;
User::LeaveIfError(iNetworkStatusProperty.Get(networkState));
TInt newNetworkState=KErrUnknown;
if (iIndicatorInfo & RMobilePhone::KIndNetworkAvailable)
newNetworkState=ESANetworkAvailable;
else
newNetworkState=ESANetworkUnAvailable;
if (newNetworkState!=networkState)
{
LOGINDICATOR2("IndicatorWatcher : New Network State %d", newNetworkState);
User::LeaveIfError(iNetworkStatusProperty.Set(newNetworkState));
}
}
// Update call-in-progress status if there has been a change
{
TInt callState;
User::LeaveIfError(iCurrentCallProperty.Get(callState));
TInt newCallState=KErrUnknown;
if ((iIndicatorInfo & RMobilePhone::KIndCallInProgress) &&
(callState <= ESACallNone))
{
newCallState=iCallStateWatcher->StartCallStateMonitor();
}
else if (!(iIndicatorInfo & RMobilePhone::KIndCallInProgress))
{
iCallStateWatcher->Cancel();
newCallState=ESACallNone;
}
if (newCallState!=callState)
{
LOGINDICATOR2("IndicatorWatcher : New Call State %d", newCallState);
User::LeaveIfError(iCurrentCallProperty.Set(newCallState));
}
}
// Issue another request
Phone().NotifyIndicatorChange(iStatus, iIndicatorInfo);
SetActive();
}
#endif
}
//
//
//
//
CCallStateWatcher::CCallStateWatcher(RMobilePhone& aPhone, TInt aPriority)
: CActive(aPriority), iPhone(aPhone)
{
CActiveScheduler::Add(this);
}
CCallStateWatcher::~CCallStateWatcher()
{
Cancel();
}
TInt CCallStateWatcher::StartCallStateMonitor()
{
TInt state=KErrUnknown;
TInt lineCount = 0;
// Find the line and call to open permanently
TInt error = iPhone.EnumerateLines(lineCount);
if (error)
return state;
for(TInt i = 0; i<lineCount; i++)
{
RPhone::TLineInfo lineInfo;
error = iPhone.GetLineInfo(i, lineInfo);
if (error)
break; // and return state unknown
error = iLine.Open(iPhone,lineInfo.iName);
if (error)
break; // and return state unknown
TInt callCount=0;
error = iLine.EnumerateCall(callCount);
if (error)
{
iLine.Close();
break;
}
// There may be more than one call - but this watcher only supports
// monitoring first call found
RLine::TCallInfo callInfo;
TBool found=EFalse;
for(TInt i=0;i<callCount;i++)
{
error = iLine.GetCallInfo(i,callInfo);
if((error==KErrNone)&&
(callInfo.iStatus!=RCall::EStatusIdle)&&
(callInfo.iStatus!=RCall::EStatusUnknown))
{
found=ETrue;
break;
}
}
if(found)
{
error = iCall.OpenExistingCall(iLine, callInfo.iCallName);
if (error)
{
// Found the line to monitor but can't open call
iLine.Close();
return state;
}
state = GetCallState();
if (state!=KErrUnknown)
{
// Now monitor for further state changes
iCall.NotifyMobileCallStatusChange(iStatus,iCallState);
SetActive();
break;
}
else
{
iCall.Close();
iLine.Close();
return state;
}
} // end of if (callCount>0)
else
{
// Close this line and look at next one
iLine.Close();
}
} // end of for loop
return state;
}
void CCallStateWatcher::RunL()
{
if (iStatus.Int() == KErrNone)
{
TInt callState;
User::LeaveIfError(RProperty::Get(KUidSystemCategory, KUidCurrentCall.iUid, callState));
TInt newCallState=KErrUnknown;
newCallState=GetCallState();
if (newCallState!=callState)
User::LeaveIfError(RProperty::Set(KUidSystemCategory,KUidCurrentCall.iUid,newCallState));
if (newCallState != KErrUnknown)
{
// Now monitor for further state changes
iCall.NotifyMobileCallStatusChange(iStatus,iCallState);
SetActive();
}
else
{
iCall.Close();
iLine.Close();
}
}
else
{
iCall.Close();
iLine.Close();
}
}
void CCallStateWatcher::DoCancel()
{
iCall.CancelAsyncRequest(EMobileCallNotifyMobileCallStatusChange);
iCall.Close();
iLine.Close();
}
TInt CCallStateWatcher::RunError(TInt /*aError*/)
{
// Release resources
iCall.Close();
iLine.Close();
return KErrNone;
}
TInt CCallStateWatcher::GetCallState()
{
TInt state=KErrUnknown;
RMobileCall::TMobileCallInfoV1 info;
RMobileCall::TMobileCallInfoV1Pckg infoPckg(info);
TInt error = iCall.GetMobileCallInfo(infoPckg);
if (error)
{
// Found the call to monitor but can't get information
return state;
}
switch (info.iStatus)
{
case RMobileCall::EStatusRinging:
state = ESACallRinging;
break;
case RMobileCall::EStatusDialling:
state = ESACallDialling;
break;
case RMobileCall::EStatusConnecting:
state = ESACallAlerting;
break;
case RMobileCall::EStatusAnswering:
state = ESACallAnswering;
break;
case RMobileCall::EStatusDisconnecting:
case RMobileCall::EStatusDisconnectingWithInband:
state = ESACallDisconnecting;
break;
case RMobileCall::EStatusConnected:
case RMobileCall::EStatusHold:
case RMobileCall::EStatusReconnectPending:
{
// Call is connected, so update with type of connected call
if((info.iValid & RMobileCall::KCallAlternating)&&
(info.iAlternatingCall != RMobilePhone::EAlternatingModeSingle))
state = ESACallAlternating;
else
{
switch (info.iService)
{
case RMobilePhone::EVoiceService:
case RMobilePhone::EAuxVoiceService:
state = ESACallVoice;
break;
case RMobilePhone::ECircuitDataService:
state = ESACallData;
break;
case RMobilePhone::EFaxService:
state = ESACallFax;
break;
default:
// Not a valid call service
break;
}
}
}
break;
default:
// must be idle state - or special idle state
state = ESACallNone;
break;
}
return state;
}
//
//
//
//
// Panic Function
//
void CIndicatorWatcher::IndicatorPanic(TWatcherPanic aPanicNumber)
{
_LIT(panicText,"Indicator Watcher");
User::Panic(panicText,aPanicNumber);
}