telephonyutils/telephonywatchers/src/watcherbase.cpp
author ivan.fildichev@opencode.com
Thu, 18 Nov 2010 15:42:16 +0200
branchopencode
changeset 88 5e27cc612ac7
parent 24 6638e7f4bd8f
permissions -rw-r--r--
Latest bug-fixes with added tests.

// 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:
//

#include "watcherbase.h"
#include "watcherlog.h"

// System includes
 #include <commsdattypesv1_1.h>
 #include <commsdat_partner.h>

 #include <sacls.h>

 using namespace CommsDat;

// User includes
#include "phoneonoff.h"

// Constants
const TInt KOneSecond = 1000000;



//
// ------> Global exports
//


//
// ------> CWatcherBase (source)
//

EXPORT_C CWatcherBase::CWatcherBase(TInt aPriority)
:	CActive(aPriority)
	{
	CActiveScheduler::Add(this);

#ifdef WATCHER_TESTING    
    //-- this section of code is used by TE_TelWatchers(Unit) test 
	TTime now;
	now.UniversalTime();
	TheSeed = now.Int64();

    //-- define properties for test purposes
    LOGCOMMON1("CTelWatcherBase : defining properties for testing");
   
    //-- For debugging purposes only, used by TE_TelWatchers(Unit).

    //- this property change (to any value) informs that CTelPhoneWatcher has re-read modem table from commdb in
    //- CTelPhoneWatcher::DoRetrieveTSYNameL(). 
    RProperty::Define(KUidSystemCategory, KUidTestProp_ModemTableRefreshed.iUid, RProperty::EInt);  
  
    //- this property changes in CTelPhoneWatcher::HandleModemChangedL()
    //- when the commsdb modem record has changed
    RProperty::Define(KUidSystemCategory, KUidTestProp_ModemRecordChanged.iUid, RProperty::EInt);
    
    //-- this property is used in CIndicatorWatcher::HandleIndicatorUpdateL()
    //-- to simulate call state change by t_watchers test
    RProperty::Define(KUidSystemCategory, KUidTestProp_CallStateChange.iUid, RProperty::EInt);

    //-- this property is used to disable and reset phone watchers
    RProperty::Define(KUidSystemCategory, KUidTestProp_WatchersDisable.iUid, RProperty::EInt);    
#endif
	}

EXPORT_C CWatcherBase::~CWatcherBase()
	{
	Cancel();
	iTimer.Close();
	iPhonePowerProperty.Close();
	}

EXPORT_C void CWatcherBase::ConstructL()
	{
	LOGCOMMON1("WatcherBase : Creating timer");
	User::LeaveIfError(iTimer.CreateLocal());

	User::LeaveIfError(iPhonePowerProperty.Attach(KUidSystemCategory, KUidPhonePwr.iUid));    
	
	// This starts the whole ball rolling
	RequestNextState();
	}

//
//
//

EXPORT_C void CWatcherBase::SuspendFor(TInt aTimeInSeconds)
	{
	LOGCOMMON1("WatcherBase : Pausing after error");
	TTimeIntervalMicroSeconds32 timeToSuspendFor = aTimeInSeconds * KOneSecond;
	iTimer.After(iStatus, timeToSuspendFor);
	State() = EBaseStateSuspending;
	SetActive();
	}

EXPORT_C void CWatcherBase::SetDisabled(const TDesC& aLogEntry, TInt aError)
	{
#ifdef _WATCHER_LOGGING_ENABLED
	TBuf8<256>  tmpBuf;
	tmpBuf.Copy(aLogEntry);
	LOGCOMMON3("Log Entry \"%S\" error %d", &tmpBuf, aError);
#else
	(void) aLogEntry;
	(void) aError;
#endif
	LOGCOMMON1("WatcherBase : Watcher is now disabled");
	State() = EBaseStateDisabled;
	}

EXPORT_C void CWatcherBase::RequestNextState()
	{
	LOGCOMMON1("WatcherBase : Requesting State Change");

	if	(State() != EBaseStateDisabled)
		{
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, KErrNone);
		SetActive();
		}
	}

EXPORT_C void CWatcherBase::WaitForPhoneToPowerUpL()
//
//	The phone is currently turned off. We must wait for it to be turned back on
//	again before the watchers can continue.
//
	{
	TInt val;	
	
	LOGCOMMON1("WatcherBase : Waiting for phone to be turned on");
	__ASSERT_DEBUG(!IsActive(), WatcherBasePanic(EUnexpectedActiveState));
	Cancel();

	//-- Subscribe to phone power state change property and wait until it changes
	iPhonePowerProperty.Subscribe(iStatus);

	// Update our state
	State() = EBaseStateWaitingForPhoneToPowerUp;

	// Set us ready to go again
	SetActive();    

	User::LeaveIfError(iPhonePowerProperty.Get(val));

	if (val != ESAPhoneOff)
		{//-- phone is already ON, complete request so that we go to RunL without waiting
		iPhonePowerProperty.Cancel();        
		LOGCOMMON1("CTelWatcherBase::WaitForPhoneToPowerUpL ??? phone is already turned ON");
		}
	}

//
//
//

EXPORT_C void CWatcherBase::RunL()
	{
	LOGCOMMON2("WatcherBase : RunL(%d)", iStatus.Int());

	switch(State())
		{
	case EBaseStateConnectingToPropertyNotifier:		
		LOGCOMMON1("WatcherBase : Attaching to Property");

		// Virtual function call back, for any subclasses that need to implement
		// any special stuff.
		HandleConnectionToChangeNotifierEstablishedL();

		// Move to next state
		State() = EBaseStatePassive;
		RequestNextState();
		break;

	case EBaseStateWaitingForPhoneToPowerUp:
		// We were waiting for the phone to become available again. We now must restart
		// this watcher from scratch.
		LOGCOMMON1("WatcherBase : Phone available again. Restarting watcher framework");
		
        //--  phone power state has changed (it must be turned ON)
        //--  retrieve its state and check.
        TInt val;
	    
        User::LeaveIfError(iPhonePowerProperty.Get(val));

        if (val == ESAPhoneOn)
        {   //-- everything OK, the phone has been turned ON, restart this watcher from scratch.
		    LOGCOMMON1("CTelWatcherBase : Phone has been turned ON. Restarting watcher framework");
            State() = EBaseStateConnectingToPropertyNotifier;
            RequestNextState();
        }
        else
        {   //-- strange situation, we were waiting for phone On and it now Off, try to wait again
            LOGCOMMON1("CTelWatcherBase : ??? Phone has been turned OFF. Continue waiting...");
            WaitForPhoneToPowerUpL();
        } 		
		break;

	case EBaseStateSuspending:
		LOGCOMMON1("WatcherBase : Completed suspension. Resuming passive state");
		
		State() = EBaseStatePassive; // Fall through

	case EBaseStatePassive: // In passive mode, so just call framework function
		HandleStateEventL(iStatus.Int());
		break;
	
	default:
	case EBaseStateDisabled:
		LOGCOMMON1("WatcherBase : RunL called in Disabled state. Ooops");
		__ASSERT_DEBUG(0, WatcherBasePanic(EUnexpectedState));
		}
	}

EXPORT_C void CWatcherBase::DoCancel()
	{
	switch(State())
		{	
	case EBaseStateSuspending:
		// Cancel outstanding timer
		iTimer.Cancel();
		break;

	case EBaseStateWaitingForPhoneToPowerUp:
		// Cancel outstanding asynchronous request
		iPhonePowerProperty.Cancel();
		break;

	default:
	case EBaseStatePassive:
	case EBaseStateConnectingToPropertyNotifier:
	case EBaseStateDisabled:
		break;
		}

	// Let other sub classes cancel their requests
	HandleCancel();
	}

EXPORT_C TInt CWatcherBase::RunError(TInt aError)
//
//	Called when RunL (or a sub-function there-of) leaves.
//
	{
#ifdef _WATCHER_LOGGING_ENABLED
	LOGCOMMON2("WatcherBase : RunError called with error of %d", aError);
#else
	(void) aError;
#endif

	// Should never be called from outside the framework
	__ASSERT_DEBUG(!IsActive(), WatcherBasePanic(EUnexpectedActiveState));
	Cancel();

	// Reset all resources in this, and the derrived class so that
	// we can start up again (from scratch) (virtual function).
	if	(State() == EBaseStatePassive)
		Reset();

	// Close our resources
	iPhonePowerProperty.Close();

	// NOTE: we don't close the timer. This was opened in ConstructL so
	// we can't close that here.

	// Have we already tried this too many times?
	if	(ErrorCountIncrement() >= KErrorRetryCount)
		{
		// We've tried to resume 3 times already and we've still not managed. Give up
		// now until a reboot.
		SetDisabled(_L("WatcherBase : RunError called too many times in succession"), KErrNone);
		}
	else
		{
		// Put us in the start up state again
		LOGCOMMON1("WatcherBase : Phone available again. Restarting watcher framework");
		State() = EBaseStateConnectingToPropertyNotifier;
		RequestNextState();
		}

	return KErrNone;
	}

//
// Panic Function
//
void CWatcherBase::WatcherBasePanic(TWatcherPanic aPanicNumber)
	{
	_LIT(panicText,"Watcher Base");
	User::Panic(panicText,aPanicNumber);
	}



//
// ------> CPhoneWatcher (source)
//


EXPORT_C CPhoneWatcher::CPhoneWatcher(TInt aPriority)
:	CWatcherBase(aPriority)
	{
	}

EXPORT_C CPhoneWatcher::~CPhoneWatcher()
	{
	delete iModemChangeObserver;
	delete iPhoneWait;
	Phone().Close();
	ETel().Close();
	}

EXPORT_C void CPhoneWatcher::ConstructL()
	{
	CWatcherBase::ConstructL();
	}

//
//
//

EXPORT_C void CPhoneWatcher::HandleStateEventL(TInt aCompletionCode)
	{
	switch(PhoneState())
		{
	case EPhoneStateRetrievingTsyName:
		// Cconstruct observers
		if	(!iPhoneWait)
			{
			iPhoneWait = new(ELeave) CPhoneOnOff(*this);
			iPhoneWait->ConstructL();
			}
		if	(!iModemChangeObserver)
			{
			iModemChangeObserver = new(ELeave) CModemChangeObserver(*this);
			iModemChangeObserver->ConstructL();
			}

		User::LeaveIfError(RetrieveTSYName());
		PhoneState() = EPhoneStateConnectingToETel;
		RequestNextState();
		break;

	case EPhoneStateConnectingToETel:
		User::LeaveIfError(ConnectToETelServer());
		PhoneState() = EPhoneStateLoadingPhoneModule;
		RequestNextState();
		break;

	case EPhoneStateLoadingPhoneModule:
		User::LeaveIfError(LoadPhoneModule());
		PhoneState() = EPhoneStateConnectingToPhone;
		RequestNextState();
		break;

	case EPhoneStateConnectingToPhone:
		User::LeaveIfError(ConnectToPhone());
		PhoneState() = EPhoneStatePassive;
		RequestNextState();
		break;
	
	case EPhoneStatePassive:
		HandlePhoneStateEventL(aCompletionCode);
		break;

	default:
		__ASSERT_DEBUG(0, WatcherBasePanic(EUnexpectedState));
		}
	}

EXPORT_C void CPhoneWatcher::Reset()
	{
	// Ensures CModemChangeObserver object stops running
	if (iModemChangeObserver)
		{
		iModemChangeObserver->Cancel();
		}

	if	(PhoneState() == EPhoneStatePassive)
		{
		// Get children to release any resources they have
		ReleasePhoneResources();
		}
	
	// Close our connections to the phone & ETel
	Phone().Close();
	ETel().Close();

	// Reset state
	PhoneState() = EPhoneStateRetrievingTsyName;
	}

//
//
//

TInt CPhoneWatcher::RetrieveTSYName()
	{
	LOGCOMMON1("PhoneWatcher : RetrieveTSYName()");
	TRAPD(error, DoRetrieveTSYNameL());
	return error;
	}

TInt CPhoneWatcher::ConnectToETelServer()
	{
	LOGCOMMON1("PhoneWatcher : ConnectToETelServer()");
	return ETel().Connect();
	}

TInt CPhoneWatcher::LoadPhoneModule()
	{
#ifdef _WATCHER_LOGGING_ENABLED
	TBuf8<256>  tmpBuf;
	tmpBuf.Copy(iTSYName);
	LOGCOMMON1("PhoneWatcher : LoadPhoneModule()");
	LOGCOMMON2("TSY Name to load is %S",&tmpBuf);
#endif

	return ETel().LoadPhoneModule(iTSYName);
	}

TInt CPhoneWatcher::ConnectToPhone()
	{
	LOGCOMMON1("PhoneWatcher : ConnectToPhone()");
	TInt error;

	RTelServer::TPhoneInfo phoneInfo;

	// Get the number of phones
	TInt phoneCount = 0;
	error = ETel().EnumeratePhones(phoneCount);
	if	(error < KErrNone)
		{
		LOGCOMMON2("PhoneWatcher : Failed to enumerate phones (%d)", error);
		return error;
		}

	LOGCOMMON2("PhoneWatcher : Counted %d 'phones'", phoneCount);

	// Iterate through all the phones
	for(TInt i=0; i<phoneCount; i++)
		{
		// Get the TSY name for the phone
		TName matchTsyName;
		
		error = ETel().GetTsyName(i, matchTsyName);
		if	(error < KErrNone)
			{
			LOGCOMMON2("PhoneWatcher : Getting TSY name failed (%d)", error);
			return error;
			}

#ifdef _WATCHER_LOGGING_ENABLED
		TBuf8<256>  tmpMatchTsyName;
		tmpMatchTsyName.Copy(matchTsyName);
		LOGCOMMON3("PhoneWatcher : TSY for phone %d is '%S'", i, &tmpMatchTsyName);
#endif

		// See if the phone belongs to the TSY
		if	(matchTsyName.CompareF(iTSYName) == 0)
			{
#ifdef _WATCHER_LOGGING_ENABLED
			TBuf8<256>  tsyNameBuf;
			tsyNameBuf.Copy(iTSYName);
			LOGCOMMON3("PhoneWatcher : %S is a match for CommDb TSY: %S", &tmpMatchTsyName, &tsyNameBuf);
#endif

			error = ETel().GetPhoneInfo(i, phoneInfo);
			if	(error < KErrNone)
				{
				LOGCOMMON2("PhoneWatcher : Getting phone info failed (%d)", error);
				return error;
				}
			break;
			}
		}

	// Connect to the specified phone
	error = Phone().Open(ETel(), phoneInfo.iName);
	if	(error < KErrNone)
		{
#ifdef _WATCHER_LOGGING_ENABLED
		TBuf8<256>  tmpBuf;
		tmpBuf.Copy(phoneInfo.iName);
		LOGCOMMON3("PhoneWatcher : Open phone %S failed (%d)", &tmpBuf, error);
#endif
		return error;
		}

#ifdef _WATCHER_LOGGING_ENABLED
	TBuf8<256>  tmpBuf;
	tmpBuf.Copy(phoneInfo.iName);
	LOGCOMMON2("PhoneWatcher : Opened 'phone' %S", &tmpBuf);
#endif

	// Indicate we're connected and to move to next state.
	return error;
	}

//
//
//

void CPhoneWatcher::DoRetrieveTSYNameL()
	{
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
	CMDBSession* db = CMDBSession::NewL(KCDVersion1_2);
#else
	CMDBSession* db = CMDBSession::NewL(KCDVersion1_1);
#endif
	CleanupStack::PushL(db);

	CMDBField<TUint32>* globalSettingsField = new(ELeave) CMDBField<TUint32>(KCDTIdModemPhoneServicesSMS);
	CleanupStack::PushL(globalSettingsField);
	globalSettingsField->SetRecordId(1);
	globalSettingsField->LoadL(*db);
	TUint32 modemId = *globalSettingsField;
	CleanupStack::PopAndDestroy(globalSettingsField);
	
	CMDBField<TDesC>* tsyField = new(ELeave) CMDBField<TDesC>(KCDTIdTsyName);
	CleanupStack::PushL(tsyField);
	tsyField->SetRecordId(modemId);
	tsyField->SetMaxLengthL(KMaxTextLength);
	tsyField->LoadL(*db);
	iTSYName = *tsyField;
	CleanupStack::PopAndDestroy(tsyField);
	
	
	// Strip any file extension
	TInt pos = iTSYName.LocateReverse('.');
	if	(pos >= 0)
		iTSYName = iTSYName.Left(pos);

#ifdef WATCHER_TESTING
	{
	TTime now;
	now.UniversalTime();
	User::LeaveIfError(RProperty::Set(KUidSystemCategory, KUidTestProp_ModemTableRefreshed.iUid, I64LOW(now.Int64())));
	}
#endif

	CleanupStack::PopAndDestroy(); // db or commsDatabase 
	}

EXPORT_C void CPhoneWatcher::PhoneIsOff()
	{
	Cancel();
	Reset(); // Kill phone resources
#ifdef	_DEBUG
	TRAPD(err, WaitForPhoneToPowerUpL());
	__ASSERT_DEBUG(err == KErrNone, WatcherBasePanic(EGeneral));
#else
	TRAP_IGNORE(WaitForPhoneToPowerUpL());
#endif
	}

EXPORT_C void CPhoneWatcher::HandleModemChangedL()
//
//	Called when the commsdb modem record has changed. Must
//	re-read the current modem and attempt to reinitialise the watcher
//	using this new modem.
//
	{
#ifdef WATCHER_TESTING
	{
	TTime now;
	now.UniversalTime();
	// State set to ETrue/EFalse
	User::LeaveIfError(RProperty::Set(KUidSystemCategory, KUidTestProp_ModemRecordChanged.iUid, I64LOW(now.Int64())));
	}
#endif

	Cancel();

	// Kill phone resources
	Reset();

	// Start us going again. This will (in turn) 
	// re-read the comms database. NOTE: Our state has been set to
	// 'EPhoneStateRetrievingTsyName' by Reset()
	RequestNextState();
	}