// Copyright (c) 2003-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:
// Implements the CSmsProtocol and its helper classes.
// 
//

/**
 @file
*/

#include <commsdattypesv1_1.h>
#include <logwrap.h>
#include "Gsmumsg.h"
#include "gsmubuf.h"
#include "Gsmuelem.h"
#include "gsmunonieoperations.h"

#include "smsprot.h"
#include "smspfacadestor.h"
#include "smspmodm.h"
#include "smspenum.h"
#include "smspread.h"
#include "smspdel.h"
#include "smspproc.h"
#include "smspmondsk.h"
#include "smspmain.h"
#include "smspver.h"
#include "smsppara.h"
#include "SmsuBackup.h"
#include <es_prot_internal.h>

using namespace CommsDat;

//
// Class implementations
//

// application port range according to 3GPP TS 23.040 v4.4.0
// chapters 9.2.3.24.3 and 9.2.3.24.4

const TInt KMin8BitPortNumber = 240;
const TInt KMax8BitPortNumber = 255;

const TInt KMin16BitPortNumber = 16000;
const TInt KMax16BitPortNumber = 16999;


/**
 *  2 phase constructor
 *  
 */
CSmsProtocolFamily * CSmsProtocolFamily::NewL()
	{
	LOGSMSPROT4("CSmsProtocolFamily::NewL [version %d.%d.%d]",
			 KSmsPrtMajorVersionNumber,
			 KSmsPrtMinorVersionNumber,
			 KSmsPrtBuildVersionNumber
			 );
	return new (ELeave)CSmsProtocolFamily;
	} // CSmsProtocolFamily::NewL


/**
 *  Implementation of the pure virtual CProtocolFamilyBase::Install().
 *  Called by the socket server after creation of this object.
 *  Does nothing.
 *  
 */
TInt CSmsProtocolFamily::Install()
	{
	LOGSMSPROT1("CSmsProtocolFamily::Install");
	return KErrNone;
	} // CProtocolFamilyBase::Install


/**
 *  Implementation of the pure virtual CProtocolFamilyBase::Remove().
 *  Called by the socket server before unloading the library for the SMS
 *  protocol family.
 *  Does nothing.
 *  
 */
TInt CSmsProtocolFamily::Remove()
	{
	LOGSMSPROT1("CSmsProtocolFamily::Remove");
	return KErrNone;
	} // CProtocolFamilyBase::Remove


/**
 *  Implementation of the pure virtual CProtocolFamilyBase::NewProtocolL().
 *  Called by the socket server to create the CSmsProtocol object.
 *  
 *  @param aSockType not used, assumes datagram.
 *  @param aProtocol not used, assumes KSmsDatagram, the only protocol provided.
 *  @return a new instance of the CSmsProtocol class.
 *  
 */
CProtocolBase * CSmsProtocolFamily::NewProtocolL(TUint /*aSockType*/,TUint /*aProtocol*/)
	{
	LOGSMSPROT1("CSmsProtocolFamily::NewProtocolL");
	return CSmsProtocol::NewL();
	} // CProtocolFamilyBase::NewProtocolL


/**
 *  Implementation of the pure virtual CProtocolFamilyBase::ProtocolList().
 *  Called by the socket server during initialisation to retrieve a list
 *  of the protocols we support.
 *  Only KSmsDatagram is supported.
 *  
 *  @return aProtocolList a pointer to an array of protocol description objects
 *  that this function creates and fills in.
 *  @return the number of protocols supported (1).
 *  
 */
TUint CSmsProtocolFamily::ProtocolList(TServerProtocolDesc *& aProtocolList)
	{
	LOGSMSPROT1("CSmsProtocolFamily::ProtocolList");

	TRAPD(ret, (aProtocolList=new(ELeave) TServerProtocolDesc[1]));
	if(ret!=KErrNone)
		{
		LOGSMSPROT2("WARNING! new TServerProtocolDesc left with %d", ret);
		return 0;
		}

	// Datagram protocol
	aProtocolList[0].iName=KSmsDatagram;
	aProtocolList[0].iAddrFamily=KSMSAddrFamily;
	aProtocolList[0].iSockType=KSockDatagram;
	aProtocolList[0].iProtocol=KSMSDatagramProtocol;
	aProtocolList[0].iVersion=TVersion(KSmsPrtMajorVersionNumber,KSmsPrtMinorVersionNumber,KSmsPrtBuildVersionNumber);
	aProtocolList[0].iByteOrder=ELittleEndian;
	aProtocolList[0].iServiceInfo=KSMSDatagramServiceInfo;
	aProtocolList[0].iNamingServices=0;
	aProtocolList[0].iSecurity=KSocketNoSecurity;
	aProtocolList[0].iMessageSize=KSMSMaxDatagramSize;
	aProtocolList[0].iServiceTypeInfo=ESocketSupport;
	aProtocolList[0].iNumSockets=KSMSNumberSockets;

	return 1;
	} // CProtocolFamilyBase::ProtocolList


/**
 *  Constructor (empty)
 */
CSmsProtocolFamily::CSmsProtocolFamily()
	{
	} // CSmsProtocolFamily::CSmsProtocolFamily


/**
 *  The single exported function, called by the socket server to create
 *  an instance of our CProtocolFamilyBase derived class.
 *  
 */
EXPORT_C CProtocolFamilyBase* InstallSMS()
	{
	LOGSMSPROT1("CSmsProtocolFamily::CSmsProtocolFamily()");

	CSmsProtocolFamily*  smsProtocolFamily(NULL);
	
	TRAP_IGNORE(smsProtocolFamily = CSmsProtocolFamily::NewL());

	return smsProtocolFamily;
	} // CSmsProtocolFamily::CSmsProtocolFamily


//
// implementation of CSmsProtocol
//


/**
 *  2 phase constructor.
 *  
 */
CSmsProtocol* CSmsProtocol::NewL()
	{
	LOGSMSPROT1("CSmsProtocol::NewL()");

	return new (ELeave) CSmsProtocol();
	} // CSmsProtocol::NewL


/**
 *  Destructor, ensures all outstanding queues are empty,
 *  all resource handles are closed, and all allocated memory freed.
 *  
 */
CSmsProtocol::~CSmsProtocol()
    {
    delete iSmsPhoneInitialization;
    delete iSmsModemNotification;

    delete iSendQueue;
	delete iSetBearer;	// referenced by iSendQueue

    delete iSmsPDURead;
	delete iReceiveMode;	// referenced by iSmsPDURead
    delete iSmsPhoneEnumeration;
    delete iWriteQueue;
    delete iDeleteQueue;


    delete iSmsReadParams;
    delete iSmsWriteParams;
    delete iSmsMonitorDiskSpace;

    delete iReassemblyStore;

    if(iSegmentationStore)
        {
        iSegmentationStore->Close();
        delete iSegmentationStore;
        }
    iFs.Close();

    iSmsMessaging.Close();
    iGsmPhone.Close();
    iEnumerationPhone.Close();
    iWritePhone.Close();
    iTelServer.Close();
	delete iBackupRestoreSession;
	delete iBootTimer;
    } // CSmsProtocol::~CSmsProtocol



/**
 *  Override of CProtocolBase::NewSAPL().
 *  Called by the server to create a new SMS service access point object.
 *  
 *  @param aSocketType the socket type
 *  @leave Leaves if aSocketType is not KSockDatagram.
 *  @leave Leaves if not enough memory is available.
 *  @return a new CSmsProvider object.
 *  
 */
CServProviderBase *CSmsProtocol::NewSAPL(TUint aSocketType)
	{
	LOGSMSPROT2("*CSmsProtocol::NewSAPL [sockettype=%d]", aSocketType);
	if (aSocketType!=KSockDatagram)
		User::Leave(KErrNotSupported);
	return CSmsProvider::NewL(*this);
	} // CProtocolBase::NewSAPL


/**
 *  Override of CProtocolBase::InitL().
 *  Called by the socket server to allow any resource initialisation
 *  by the protocol.
 *  
 *  The following tasks are performed:
 *  - All member objects constructed (deferred from ConstructL() for efficiency reasons; see comment there)
 *  - TSY Name read from Comms Database
 *  - Event notification state machines started
 *  - Modem checked to see if it is already connected, and if so, a number of
 *  state machines started (otherwise wait until we receive notification of connection).
 *  
 */
void CSmsProtocol::InitL(TDesC& /*aTag*/)
	{
	LOGSMSPROT1("CSmsProtocol::InitL");

	User::LeaveIfError(iFs.Connect());

	iReassemblyStore=CFacadeSmsReassemblyStore::NewL(iFs, *this);
	iReassemblyStore->InitL();

	LOGSMSPROT1("CSmsProtocol::InitL Constructing members");

	ReadConfigurableSettingsL();

	iBootTimer = CSmsProtocolBootTimer::NewL(*this);
	iBootTimer->Start(iSmsSettings.BootTimerTimeout().Int());

	iSegmentationStore=CSmsSegmentationStore::NewL(iFs);

	iSetBearer=CSmspSetBearer::NewL(iSmsSettings, iSmsMessaging, KSmsSessionPriority);

	iReceiveMode = CSmspReceiveMode::NewL(iSmsSettings, iSmsMessaging, iMobileSmsCaps, KSmsSessionPriority);

	iSmsModemNotification=CSmsModemNotification::NewL(*this);

	iSendQueue = CSmspSendQueue::NewL(*this, *iSegmentationStore, iSmsSettings, iMobileSmsCaps, iSmsMessaging, KSmsSessionPriority, *iSetBearer);

	iSmsMonitorDiskSpace=CSmsMonitorDiskSpace::NewL(*this, iSmsMessaging,iFs);

	iSmsPDURead=CSmsPDURead::NewL(*this, iSmsSettings, iSmsMessaging,*iReassemblyStore,*iSegmentationStore, iMobileSmsCaps, KSmsSessionPriority, *iReceiveMode, *iSmsMonitorDiskSpace);

	iSmsPhoneInitialization = new (ELeave) CSmsPhoneInitialization(iSmsMessaging, iGsmPhone, *iSmsPDURead, iMobileSmsCaps, KSmsSessionPriority, iSetBearer);

	iBackupRestoreSession = CBackupAndRestore::NewL(*this);

/* THIS CODE SHOULD NEVER BE NECESSARY - who could have called CProtocolBase::InitL() if not ESOCK, which means
 * a running C32 already. However, if this is a wrong analysis then reinstate the code but with an explanatory comment
#if defined (__WINS__)
	// Make sure C32 is started under WINS
	TInt ret=StartC32();
	if (ret!=KErrAlreadyExists)
		User::LeaveIfError(ret);
#endif
*/
	LOGSMSPROT1("CSmsProtocol::InitL Querying CommDb");

	// Read the global modem ID setting from  Cooms Database
	TUint32 modemId = 0;

	
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
	CMDBSession* sess = CMDBSession::NewL(KCDVersion1_2);
#else
	CMDBSession* sess = CMDBSession::NewL(KCDVersion1_1);
#endif
	CleanupStack::PushL(sess);

	CMDBField<TUint32>* globalSettingField = new(ELeave) CMDBField<TUint32>(KCDTIdModemPhoneServicesSMS);
	CleanupStack::PushL(globalSettingField);
	globalSettingField->SetRecordId(1);
	globalSettingField->LoadL(*sess);
	modemId = *globalSettingField;
	CleanupStack::PopAndDestroy(globalSettingField);

	CMDBField<TDesC>* tsyField = new(ELeave) CMDBField<TDesC>(KCDTIdTsyName);
	CleanupStack::PushL(tsyField);
	tsyField->SetRecordId(modemId);
	tsyField->LoadL(*sess);
	iGsmTsyName = *tsyField;
	CleanupStack::PopAndDestroy(tsyField);

	CleanupStack::PopAndDestroy(sess);

#ifdef _SMS_LOGGING_ENABLED
	TBuf8<KCommsDbSvrMaxFieldLength> buf8;
	buf8.Copy(iGsmTsyName);
	LOGSMSPROT3("CSmsProtocol::InitL [modemId=%d tsy=%S]",
				modemId, &buf8);
#endif

	// Start event notification state machines
	iSmsModemNotification->Start();

	// Intialise the SmsStack to the state that the phone has been found in
	iModemDetection=iSmsModemNotification->ModemState();

	if (iModemDetection == RPhone::EDetectedPresent && !iBackupRestoreSession->IsBackupOrRestoreInProgress())
		{
		DoPowerUpL(); //Call the leaving version because powering up must work here
		}
	else
		{
		PowerDown();
		}

	//
	// Define the Disk Space Monitor P&S variable...
	//
	TInt  ret;

	TSecurityPolicy  readPolicy(ECapabilityReadDeviceData);
	TSecurityPolicy  writePolicy(ECapabilityWriteDeviceData);
	
	ret = RProperty::Define(KUidPSSMSStackCategory, KUidPSSMSStackDiskSpaceMonitorKey,
							KUidPSSMSStackDiskSpaceMonitorKeyType, readPolicy, writePolicy);
	if (ret != KErrNone  &&  ret != KErrAlreadyExists)
		{
		User::Leave(ret);
		}

	User::LeaveIfError(RProperty::Set(KUidPSSMSStackCategory,
									  KUidPSSMSStackDiskSpaceMonitorKey, ESmsDiskSpaceAvailable));
	} // CProtocolBase::InitL


/**
 *  Override of CProtocolBase::StartL().
 *  Called by the socket server to indicate all bindings are complete
 *  and that datagram processing can begin.
 *  
 *  Binding is not supported so this indication is not important to us.
 *  
 */
void CSmsProtocol::StartL(void)
	{
	LOGSMSPROT1("CSmsProtocol::StartL");
	} // CSmsProtocol::StartL


/**
 *  Override of CProtocolBase::Identify().
 *  Called by the socket server to obtain a description of the SMS protocol.
 *  
 */
void CSmsProtocol::Identify(TServerProtocolDesc *aDesc)const
	{
	LOGSMSPROT1("CSmsProtocol::Identify");

	aDesc->iName=KSmsDatagram;
	aDesc->iAddrFamily=KSMSAddrFamily;
	aDesc->iSockType=KSockDatagram;
	aDesc->iProtocol=KSMSDatagramProtocol;
	aDesc->iVersion=TVersion(KSmsPrtMajorVersionNumber,KSmsPrtMinorVersionNumber,KSmsPrtBuildVersionNumber);
	aDesc->iByteOrder=ELittleEndian;
	aDesc->iServiceInfo=KSMSDatagramServiceInfo;
	aDesc->iNamingServices=0;
	aDesc->iSecurity=KSocketNoSecurity;
	aDesc->iMessageSize=KSMSMaxDatagramSize;
	aDesc->iServiceTypeInfo=0;
	aDesc->iNumSockets=KSMSNumberSockets;
	} // CProtocolBase::Identify


/**
 *  Override of CProtocolBase::NewHostResolverL().
 *  Called by socket server to create a host resolver.
 *  No host resolver service is provided by this SMS protocol.
 *  
 *  @leave Panics if called.
 *  
 */
CHostResolvProvdBase *CSmsProtocol::NewHostResolverL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::NewHostResolverL");
    SmspPanic(ESmspCantCreateHostResolver);
    return NULL;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::NewServiceResolverL().
 *  Called by socket server to create a new service resolver.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
CServiceResolvProvdBase *CSmsProtocol::NewServiceResolverL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("*CSmsProtocol::NewServiceResolverL");
    SmspPanic(ESmspCantCreateServiceResolver);
    return NULL;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::NewNetDatabaseL().
 *  Called by socket server to create a new network database.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
CNetDBProvdBase* CSmsProtocol::NewNetDatabaseL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::NewNetDatabaseL");
    SmspPanic(ESmspCantCreateNetDatabase);
    return NULL;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::BindL().
 *  Called by next protocol above wishing to bind to the SMS protocol.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
void CSmsProtocol::BindL(CProtocolBase* /*aProtocol*/,TUint /*aId*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::BindL");
    SmspPanic(ESmspCantBind);
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::BindToL().
 *  Called by socket server when a lower protocol must bind to the
 *  SMS protocol.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
void CSmsProtocol::BindToL(CProtocolBase* /*aProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::BindToL");
    SmspPanic(ESmspCantBindTo);
    BULLSEYE_RESTORE
    }

/**
 *  Reads timeout value for send operation from configuration file
 *  
 *  @leave Leaves if configuration file cannot be read
 *  @leave Leaves if value not greater than zero
 */
void CSmsProtocol::ReadConfigurableSettingsL()
	{
	CESockIniData* ini = NULL;
	TRAPD(ret, ini=CESockIniData::NewL(_L("smswap.sms.esk")));
	if(ret!=KErrNone)
		{
		LOGSMSPROT2("esk read failed, error code = [%d]", ret);
		User::Leave(ret);
		}

	CleanupStack::PushL(ini);

	TInt var(0);
	if(ini->FindVar(_L("customTimeoutSettings"),_L("sendTryTimeout"),var))
		{
		if (var > 0)
			{
			LOGSMSPROT2("sendTryTimeout [%d]", var);
			iSmsSettings.SetSendTryTimeout(var);
			}
		else
			{
			User::Leave(KErrArgument);
			}
		}

	if(ini->FindVar(_L("customTimeoutSettings"),_L("bootTimerTimeout"),var))
		{
		if (var > 0)
			{
			LOGSMSPROT2("bootTimerTimeout [%d]", var);
			iSmsSettings.SetBootTimerTimeout(var);
			}
		else
			{
			User::Leave(KErrArgument);
			}
		}

	CleanupStack::PopAndDestroy(ini);
	} // CSmsProtocol::ReadConfigurableSettingsL


/**
 *  Override of CProtocolBase::Send().
 *  A down call from a protocol bound above to send a datagram.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
TInt CSmsProtocol::Send(TDes8& /*aDes*/,TSockAddr* /*aTo*/,TSockAddr* /*aFrom*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::Send");
    SmspPanic(ESmspCantSend);
    return KErrNotSupported;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::Process().
 *  An up call from a protocol bound below to process a datagram.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
void CSmsProtocol::Process(TDes8& /*aDes*/,TSockAddr* /*aFrom*/,TSockAddr* /*aTo*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::Process");
    SmspPanic(ESmspCantProcess);
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::GetOption().
 *  A down call from a protocol bound above to get a protocol option.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
TInt CSmsProtocol::GetOption(TUint /*aLevel*/,TUint /*aName*/,TDes8 & /*aOption*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::GetOption");
    // SmspPanic(ESmspCantGetOption);
    return KErrNotSupported;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::SetOption().
 *  A down call from a protocol bound above to set a protocol option.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
TInt CSmsProtocol::SetOption(TUint /*aLevel*/,TUint /*aName*/,const TDesC8& /*option*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::SetOption");
    //SmspPanic(ESmspCantSetOption);
    return KErrNotSupported;
    BULLSEYE_RESTORE
    }

/**
 *  Override of CProtocolBase::Error().
 *  An up call from a protocol bound below to indicate an error.
 *  Not supported by this protocol.
 *  
 *  @leave Panics if called.
 *  
 */
void CSmsProtocol::Error(TInt /*aError*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGSMSPROT1("CSmsProtocol::Error");
    SmspPanic(ESmspCantError);
    BULLSEYE_RESTORE
    }

/**
 *  Adds an observer to the protocol's observer list.
 *  Used by CSmsProvider and CWapSmsProtocol.
 *  
 *  @leave Leaves if not enough memory is available.
 *  
 */
void CSmsProtocol::AddSmsMessageObserverL(MSmsMessageObserver& aObserver)
    {
    LOGSMSPROT2("CSmsProtocol::AddSmsMessageObserverL [aObserver=0x%08x]", &aObserver);
    iSmsMessageObserverList.AppendL(&aObserver);
    SetClosingDown(EFalse);
    }

/**
 *  Binds an SMS observer to the protocol.
 *  The protocol ensures there are no providers with duplicate addresses.
 *  Used by CSmsProvider::SetLocalName(), and SMS WAP protocol.
 *  
 *  @param aObserver the observer.
 *  @param aSmsAddr the local address of the observer.
 *  @return KErrNone on success, or KErrAlreadyExists if a duplicate exists.
 *  
 */
TInt CSmsProtocol::BindSmsMessageObserver(MSmsMessageObserver& aObserver,const TSmsAddr& aSmsAddr)
	{
	LOGSMSPROT2("CSmsProtocol::BindSmsMessageObserver 0x%08x", &aObserver);
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));
	TInt ret=KErrNone;

	if (!SmsAddrIsAlreadyUsed(&aObserver,aSmsAddr))
		{
		aObserver.SetLocalAddress(aSmsAddr);
		OrderSmsMessageObserver(aObserver);

		// If not in the powered-up state then the SAR store is closed. When the phone does power-up
		// then anything waiting in the store will get processed
		if(CheckPoweredUp() == KErrNone)
			{
			//
			// check the SAR store for any complete messages.
			// if there are any, send them to the observer
			//
			TRAP(ret, ProcessCompleteSmsMessagesL());
			if(ret != KErrNone)
				{
				LOGSMSPROT2("WARNING! CSmsProtocol::ProcessCompleteSmsMessagesL left with %d", ret);
				}
			}
		}
	else
		{
		ret=KErrAlreadyExists;
		}
	return ret;
	} // CSmsProvider::SetLocalName


/**
 *  Removes an observer from the observer list.
 *  Observers should at least call this method in their destructors.
 *  
 */
void CSmsProtocol::RemoveSmsMessageObserver(const MSmsMessageObserver& aObserver)
    {
    LOGSMSPROT2("CSmsProtocol::RemoveSmsMessageObserver 0x%08x", &aObserver);
    __ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));
    TInt index=ObserverIndex(aObserver);
    iSmsMessageObserverList.Delete(index);
    }

/**
 *  Handles a request from a SAP to send a SMS message.
 *  Ensures there is a current connection to the modem and queues the message.
 *  Completes with an error immediately if CheckPoweredUp() returns an error code
 */
void CSmsProtocol::SendSmsMessage(CSmsMessage* aSmsMessage,MSmsMessageObserver& aObserver,TUint aOptions)
	{
	LOGSMSPROT2("CSmsProtocol::SendSmsMessage [aObserver=0x%X]", &aObserver);
	__ASSERT_DEBUG(ObserverIsPresent(aObserver), SmspPanic(ESmspMessageObserverNotFound));

	// Ensure the modem is connected and initialized
	const TInt err = CheckPoweredUp();

	if (err != KErrNone)
		{
		// Nope, complete immediately
		aObserver.MessageSendCompleted(err);
		delete aSmsMessage;
		}
	else
		{
		iSendQueue->Queue(aSmsMessage, aObserver, aOptions);
		}
	}

TInt CSmsProtocol::CheckPoweredUp() const
    {
    TInt err = KErrNone;
    
    if( iModemDetection == RPhone::EDetectedNotPresent || iState == EPoweredDown )
        {
        err = KErrDisconnected;
        }
    else if( iBackupRestoreSession->IsBackupOrRestoreInProgress() )
        {
        err = KErrAccessDenied;
        }
    else
        {
        err = iSmsPhoneInitialization->Initialized();
        }
    if( err != KErrNone )
        {
        LOGSMSPROT4("CSmsProtocol::CheckPoweredUp [err=%d, iModemDetection=%d, IsBackupOrRestoreInProgress=%d]", err, iModemDetection, iBackupRestoreSession->IsBackupOrRestoreInProgress());
        }    
    return err;
    }

/**
 *  Cancels a previous request to send an SMS message.
 *  Searches the send queue for the message.  If it is found at the
 *  head of the queue the sending state machine is cancelled, which
 *  in turn will callback to the protocol and delete the message.
 *  If the message is elsewhere in the queue, it is simply removed
 *  from the queue and the observer notified.
 *  
 *  @leave Panics in DEBUG if the message is not found in the queue.
 *  
 */
void CSmsProtocol::CancelSendSmsMessage(MSmsMessageObserver& aObserver,TBool)
	{
	LOGSMSPROT2("CSmsProtocol::CancelSendSmsMessage 0x%08x", &aObserver);

	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	iSendQueue->CancelObserver(aObserver);
	} // CSmsProtocol::CancelSendSmsMessage


/**
 *  Handles a request from a SAP to enumerate the SMS messages stored on the phone.
 */
void CSmsProtocol::EnumeratePhone(MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::EnumeratePhone");
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	// Ensure the modem is connected and initialized
	const TInt err = CheckPoweredUp();

	if (err != KErrNone)
		aObserver.EnumeratePhoneCompleted(err);
	else
		{
		if (iPhoneEnumerationObserver!=NULL)
			{
			aObserver.EnumeratePhoneCompleted(KErrInUse);
			}
		else
			{
			iPhoneEnumerationObserver=&aObserver;
			iSmsPhoneEnumeration->Start();
			}
		}
	} // CSmsProtocol::EnumeratePhone


/**
 *  Cancels a previous request to enumerate the message on the phone memory.
 *  
 *  @leave Panics in DEBUG if the SAP is invalid.
 *  
 */
void CSmsProtocol::CancelEnumeratePhone(MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::CancelEnumeratePhone");

	__ASSERT_DEBUG(ObserverIsPresent(aObserver), SmspPanic(ESmspMessageObserverNotFound));
	__ASSERT_DEBUG(iPhoneEnumerationObserver==NULL || &aObserver==iPhoneEnumerationObserver, SmspPanic(ESmspMessageWrongObserver));

	if (iSmsPhoneEnumeration != NULL)
		{
		iSmsPhoneEnumeration->Cancel();
		}

	iReassemblyStore->DeleteEnumeratedSIMEntries();

    (void) aObserver;
	} // CSmsProtocol::CancelEnumeratePhone


TInt CSmsProtocol::ExternalizeEnumeratedMessagesL(CSmsProvider& aProvider,TInt& aCount)
	{
	return iReassemblyStore->ExternalizeEnumeratedMessagesL(aProvider, aCount);
	} // CSmsProtocol::ExternalizeEnumeratedMessagesL


/**
 *  Handles a request to write an SMS message to the phone's memory.
 *  Completes with an error immediately if CheckPoweredUp() returns an error code
 *  Otherwise the message is added to the tail of the write queue.
 */
void CSmsProtocol::WriteSmsMessage(CSmsMessage* aSmsMessage,MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::WriteSmsMessage");
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	const TInt err = CheckPoweredUp();

	if (err != KErrNone)
		{
		aObserver.MessageWriteCompleted(err);
		delete aSmsMessage;
		}
	else
		{
		iWriteQueue->Queue(aSmsMessage, aObserver, NULL);
		}
	} // CSmsProtocol::WriteSmsMessage


void CSmsProtocol::CancelWriteSmsMessage(MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::CancelWriteSmsMessage()");

	if (iWriteQueue != NULL)
		iWriteQueue->CancelObserver(aObserver);
	} // CSmsProtocol::CancelWriteSmsMessage


/**
 *  Handles a request from a SAP to delete a message from the phone memory.
 *  Completes with an error immediately if CheckPoweredUp() returns an error code
 *  If the cached image of the phone's memory is not yet initialised, complete the
 *  request with KErrNotReady.
 *  Otherwise match the message in the phone image and queue the message for deletion.
 *  
 *  @note aSmsMessage is destroyed from memory on completion.
 *  
 */
void CSmsProtocol::DeleteSmsMessage(CSmsMessage* aSmsMessage,MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::DeleteSmsMessage");
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	// Ensure the modem is connected and initialized
	const TInt err = CheckPoweredUp();

	if (err != KErrNone)
		{
		// Not connected or backup running
		aObserver.MessageDeleteCompleted(err);
		}
	else if (aSmsMessage->iSlotArray.Count()==0)
		aObserver.MessageDeleteCompleted(KErrArgument);
	else
		DeletePDUs(aSmsMessage->iSlotArray,&aObserver);

	delete aSmsMessage;
	} // CSmsProtocol::DeleteSmsMessage


/**
 *  Cancels a previous request to delete a message from the phone memory.
 *  
 *  @leave Panics in DEBUG if the SAP is invalid.
 *  
 */
void CSmsProtocol::CancelDeleteSmsMessage(MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::CancelDeleteSmsMessage");

	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	iDeleteQueue->CancelObserver(aObserver);
	} // CSmsProtocol::CancelDeleteSmsMessage


/**
 *  Handles a request from a SAP to read the SMS parameters stored on the phone.
 *  Completes with an error immediately if CheckPoweredUp() returns an error code
 *  If TSY doesn't support retrieving of parameters, the request is completed
 *  immediately with KErrNotSupported.
 *  The request is completed with KErrNoMemory if creation of CSmsReadParams
 *  object fails.
 *  The request is completed with KErrInUse if another SAP is reading.
 *  Otherwise, the reading process is started.
 *  
 */
void CSmsProtocol::ReadSmsParameters(MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::ReadSmsParameters");
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));
	TInt ret = CheckPoweredUp();

	if (ret == KErrNone)
		{
		if(iMobileSmsCaps.iSmsControl & RMobileSmsMessaging::KCapsGetSmspList)
			{
			if (iSmsReadParams==NULL)
				{
				TRAP(ret,(iSmsReadParams=CSmsReadParams::NewL(*this,iSmsSettings,iSmsMessaging)));
				}
			else if (iSmsReadParams->IsActive())
				{
				ret = KErrInUse;
				}
			}
		else
			ret = KErrNotSupported;
		}

	if(ret != KErrNone)
		aObserver.ReadSmsParamsCompleted(ret, NULL);
	else
		iSmsReadParams->Start(aObserver);
	} // CSmsProtocol::ReadSmsParameters


/**
 *  Cancels a previous request to read SMS parameters.
 *  
 */
void CSmsProtocol::CancelReadSmsParams()
	{
	LOGSMSPROT1("CSmsProtocol::CancelReadSmsParams");

	if(iSmsReadParams != NULL)
		iSmsReadParams->Cancel();
	} // CSmsProtocol::CancelReadSmsParams


/**
 *  Handles a request to write an SMS parameters to the phone's memory.
 *  Completes with an error immediately if CheckPoweredUp() returns an error code
 *  If TSY doesn't support retrieving and writing of parameters, the request is completed
 *  immediately with KErrNotSupported.
 *  The request is completed with KErrInUse if another SAP is reading or writing.
 *  The request is completed with KErrNoMemory if creation of CSmsReadParams or
 *  CSmsWriteParams object fails.
 *  Otherwise, the writing process is started.
 *  
 */
void CSmsProtocol::WriteSmsParameters(CMobilePhoneSmspList* aMobilePhoneSmspList,MSmsMessageObserver& aObserver)
	{
	LOGSMSPROT1("CSmsProtocol::WriteSmsParameters");
	__ASSERT_DEBUG(ObserverIsPresent(aObserver),SmspPanic(ESmspMessageObserverNotFound));

	TInt ret = CheckPoweredUp();

	if (ret == KErrNone)
		{
		if(iMobileSmsCaps.iSmsControl & RMobileSmsMessaging::KCapsGetSmspList && iMobileSmsCaps.iSmsControl & RMobileSmsMessaging::KCapsSetSmspList)
			{
			if (iSmsReadParams==NULL)
				{
				TRAP(ret,(iSmsReadParams=CSmsReadParams::NewL(*this,iSmsSettings,iSmsMessaging)));
				}
			else if (iSmsReadParams->IsActive())
				ret=KErrInUse;
			}
		else
			ret=KErrNotSupported;
		}

	if (ret==KErrNone)
		{
		if (iSmsWriteParams==NULL)
			{
			TRAP(ret,(iSmsWriteParams=CSmsWriteParams::NewL(*this,iSmsSettings,iSmsMessaging,*iSmsReadParams)));
			}
		else if (iSmsWriteParams->IsActive())
			ret=KErrInUse;
		}

	if (ret==KErrNone)
		iSmsWriteParams->Start(aObserver,aMobilePhoneSmspList);
	else
		{
		aObserver.WriteSmsParamsCompleted(ret);
		delete aMobilePhoneSmspList;
		}
	} // CSmsProtocol::WriteSmsParameters


/**
 *  Cancels a previous request to write SMS parameters.
 *  
 */
void CSmsProtocol::CancelWriteSmsParams()
	{
	LOGSMSPROT1("CSmsProtocol::CancelWriteSmsParams");

	if(iSmsWriteParams != NULL)
		iSmsWriteParams->Cancel();
	} // CSmsProtocol::CancelWriteSmsParams


/**
 *  Processes a received SMS message by matching it to an observer and
 *  notifying the observer of the new message.
 *  
 *  If the message has been matched to a message previously sent by the protocol
 *  (i.e. a status report from a previous submit), then the original message and address
 *  are supplied.  Matching priority in this case is given to the original sender.
 *  
 *  If the message remains in phone or SIM memory, the message is sent to the "receive any"
 *  observer, otherwise the match based on the message and address of the observer.
 *  
 *  @param aSmsMessage the received message
 *  @param aOriginalSmsAddr pointer to the address of the sender of a previously sent
 *  message matched to the received one (e.g. status report). Null if not matched.
 *  @param aOriginalSmsMessage pointer to a message previously sent matched to the
 *  received one (e.g. status report).	Null if not matched.
 *  @param aDes user data for the deliver report acknowledging this message to the SC.
 *  Filled in by the observer.
 *  @return KErrNone if the observer handled the message successfully, otherwise an error.
 *  
 */
TInt CSmsProtocol::ProcessMessageL(const CSmsMessage& aSmsMessage,const TSmsAddr* aOriginalSmsAddr,
		const CSmsMessage* /*aOriginalSmsMessage*/,TDes& aDes)
	{
	LOGSMSPROT1("CSmsProtocol::ProcessMessage");

	MSmsMessageObserver* observer=NULL;
	if (aOriginalSmsAddr!=NULL)
		{
		// Status report and original message matched - send to original sender as priority
		observer=MatchSmsAddressToObserver(*aOriginalSmsAddr);
		if (observer==NULL)
			{
			// Sender no longer present or is read only - match on received message as usual
			observer=MatchSmsMessageToObserver(aSmsMessage);
			}
		}
	else if (iPhoneEnumerationObserver==NULL  ||  IsAppPortSMS(aSmsMessage ))
		{
		// Only match if not currently enumerating or if it is meant for an app port.
		observer = MatchSmsMessageToObserver(aSmsMessage);
		}

	LOGSMSPROT2("CSmsProtocol::ProcessMessage [observer=0x%08x]",observer);
	TInt ret=KErrNone;

	if (observer!=NULL)
		{
		TBool isAppPortSms = IsAppPortSMS(aSmsMessage );

		if(isAppPortSms && iBootTimer !=NULL && iBootTimer->IsActive() && observer->GetLocalAddress().SmsAddrFamily() == ESmsAddrRecvAny)
			{
			ret = KErrNotReady;
			}
		else
			{
			ret=observer->MessageReceived(aSmsMessage,aDes);
			}

		if(ret == KErrNone && (observer->ClientConfirmsMessage() == EFalse)  && observer->GetLocalAddress().SmsAddrFamily() != ESmsAddrRecvAny)
		{
			iReassemblyStore->DeleteMessageL(aSmsMessage, EFalse);
			ret=KErrInUse;                
			// used to signal to client that there is nothing to be done on reassembly store
			// and code different from KErrNone will do - won't be propagated
		}
		else if(ret == KErrNotFound && observer->GetLocalAddress().SmsAddrFamily() != ESmsAddrRecvAny)
			{
			if ( iBootTimer && !iBootTimer->IsActive() && (aOriginalSmsAddr==NULL || aOriginalSmsAddr->SmsAddrFamily() != ESmsAddrRecvAny))
 				{
				TSmsAddr addr;
				addr.SetSmsAddrFamily(ESmsAddrRecvAny);
				ret = ProcessMessageL(aSmsMessage, &addr, NULL, aDes);
				}
			}
		}
	else
		{
        ret=KErrNotReady; // Observers are not ready
		}
	return ret;
	} // CSmsProtocol::ProcessMessageL


/**
 *  Deletes one or more PDUs from phone or SIM memory.
 *  The PDUs are added to the deletion queue.  If this queue is empty, the deletion
 *  state machine is started.
 *  
 *  @param aSlotArray
 */
void CSmsProtocol::DeletePDUs(const CArrayFix<TGsmSmsSlotEntry>& aSlotArray, MSmsMessageObserver* aObserver)
	{
	LOGSMSPROT3("CSmsProtocol::DeletePDUs [count=%d aObserver=0x%08X", aSlotArray.Count(), aObserver);
	__ASSERT_DEBUG(aSlotArray.Count() != 0, SmspPanic(KSmspSlotArrayEmpty));

	for(TInt i=0; i< aSlotArray.Count() ;i++)
		{
		LOGSMSPROT3("CSmsProtocol::DeletePDUs index: %d store %S", aSlotArray[i].iIndex, &aSlotArray[i].iStore);
		}

	if (iDeleteQueue != NULL && aSlotArray.Count() != 0)
		{
		iDeleteQueue->Queue(aSlotArray, aObserver);
		}
	} // CSmsProtocol::DeletePDUs

/**
 *  Called when the modem detection state has changed.
 *  If a modem has connection has just been made, a number of event monitoring
 *  state machines are restarted, and as is the bearer setting state machine.
 *  If the the modem connection was lost, outstanding operations are cancelled.
 *  
 */
void CSmsProtocol::ModemNotificationCompleted(TInt aStatus,
											  RPhone::TModemDetection aNewState)
	{
	LOGSMSPROT3("CSmsProtocol::ModemNotificationCompleted(): aStatus=%d, aNewState=%d",
				aStatus, aNewState);

	TBool stateChanged = EFalse;

	if (aStatus==KErrNone)
		{
		switch (iModemDetection)
			{
			//
			// it goes from OFF to ON
			//
			case RPhone::EDetectedNotPresent:
			case RPhone::EDetectedUnknown:
				{
				LOGSMSPROT1("RPhone::EDetectedNotPresent: [OFF -> ON]");
				if (aNewState==RPhone::EDetectedPresent)
					{
					// There is a new modem connection
					iModemDetection=aNewState;
					stateChanged = ETrue;
					}
				break;
				}
			//
			// it goes from ON to OFF
			//
			case RPhone::EDetectedPresent:
				{
				LOGSMSPROT1("RPhone::EDetectedPresent: [ON -> OFF]");
				if (aNewState!=RPhone::EDetectedPresent)
					{
					// Ah, lost our modem - cancel outstanding operations
					iModemDetection=aNewState;
					stateChanged = ETrue;
					}
				break;
				}
			default:
				{
				__ASSERT_DEBUG(EFalse,SmspPanic(KSmspPanicUnknownModemDetection));
				}
			}

		if (iModemDetection == RPhone::EDetectedPresent && !iBackupRestoreSession->IsBackupOrRestoreInProgress())
			{
			if (stateChanged || iState == EPoweredDown)
				{
				PowerUp();
				}
			}
		else if (stateChanged || iState != EPoweredDown)
			{
			PowerDown();
			}
		}
	} // CSmsProtocol::ModemNotificationCompleted


void CSmsProtocol::DiskSpaceMonitorStateChange(TSmsDiskSpaceMonitorStatus aStatus)
/**
 * Called when the Disk Space Monitor state has changed.
 */
	{
	LOGSMSPROT2("CSmsProtocol::DiskSpaceMonitorStateChange(): aStatus=%d", aStatus);

	RProperty::Set(KUidPSSMSStackCategory, KUidPSSMSStackDiskSpaceMonitorKey, aStatus);
	} // CSmsProtocol::DiskSpaceMonitorStateChange


/**
 *  Called when all message send requests have completed.
 *  Increments the number of segmentation store accesses and purges the store
 *  if it exceeds CSmsProtocol::KNumSARStoreAccessesBeforePurging.
 */
void CSmsProtocol::MessageSendCompleted(TInt aStatus)
	{
	LOGSMSPROT3("*** CSmsProtocol::MessageSendCompleted [aStatus=%d iNumSegmentationStoreAccesses=%d]", aStatus, iNumSegmentationStoreAccesses);
    (void) aStatus;
	iNumSegmentationStoreAccesses++;
	if (iNumSegmentationStoreAccesses>=KNumSARStoreAccessesBeforePurging)
		{
		LOGSMSPROT1("iSegmentationStore->PurgeL Start");
		TRAPD(ret, iSegmentationStore->PurgeL(iSmsSettings.KSegmentationLifetimeMultiplier(),EFalse));
		if(ret!=KErrNone)
			{
			// we need to close the file because the function
			// left with the file opened
			// iSegmentationStore->CloseFile();
			LOGSMSPROT2("WARNING! iSegmentationStore->PurgeL left with %d", ret);
			}
		iNumSegmentationStoreAccesses=0;
		LOGSMSPROT2("iSegmentationStore->PurgeL End [ret=%d]", ret);
		}
	} // CSmsProtocol::KNumSARStoreAccessesBeforePurging


/**
 *  Called when the state machine for enumerating messages on the
 *  phone has completed.  Notifies the SAP that made the request.
 *  
 */
void CSmsProtocol::PhoneEnumerationCompleted(TInt aStatus)
	{
	LOGSMSPROT1("CSmsProtocol::PhoneEnumerationCompleted");
	iPhoneEnumerationObserver->EnumeratePhoneCompleted(aStatus);
	} // CSmsProtocol::PhoneEnumerationCompleted


/**
 *  Called by CProtocolBase::Close() to handle any delayed shutdown.
 *  If there are messages in the deletion queue then the shut down flag
 *  is set, otherwise CProtocolBase::CanClose() is called to finish closing.
 *  
 */
void CSmsProtocol::CloseNow()
	{
	LOGSMSPROT1("CSmsProtocol::CloseNow");
	if (iDeleteQueue != NULL && iDeleteQueue->IsActive())
		SetClosingDown(ETrue);
	else
		CanClose();
	} // CSmsProtocol::CloseNow


/**
 *  Private constructor.
 *  
 */
CSmsProtocol::CSmsProtocol()
	:iSmsMessageObserverList(8)
	,iModemDetection(RPhone::EDetectedUnknown)
	{
	iSmsSettings.SetDeletePDUsFromSIM(EFalse);
	iSmsSettings.SetDeletePDUsFromPhoneStores(EFalse);
	iSmsSettings.SetDeletePDUsFromCombinedStores(EFalse);
	iNext8BitPort=KMin8BitPortNumber;
	iNext16BitPort=KMin16BitPortNumber;
	} // CSmsProtocol::CSmsProtocol


/**
 *  Returns the index of an observer in the observer list.
 *  
 */
TInt CSmsProtocol::ObserverIndex(const MSmsMessageObserver& aObserver) const
	{
	LOGSMSPROT1("CSmsProtocol::ObserverIndex()");

	TInt count=iSmsMessageObserverList.Count();
	TInt index=0;
	for (; index<count; index++)
		if (iSmsMessageObserverList[index]==&aObserver)
			break;
	return index;
	} // CSmsProtocol::ObserverIndex


/**
 *  Checks if an SMS address type is a duplicate of an existing SAP / observer.
 *  
 */
TBool CSmsProtocol::SmsAddrIsAlreadyUsed(const MSmsMessageObserver* aObserver,const TSmsAddr& aSmsAddr)const
	{
	LOGSMSPROT1("CSmsProtocol::SmsAddrIsAlreadyUsed()");

	TBool isduplicate=EFalse;
	TInt count=iSmsMessageObserverList.Count();
	for (TInt i=0; (i<count) && (!isduplicate); i++)
		isduplicate=(iSmsMessageObserverList[i]->SmsAddrIsDuplicate(aObserver,aSmsAddr));
	return isduplicate;
	} // CSmsProtocol::SmsAddrIsAlreadyUsed


/**
 *  A utility class for prioritizing the SMS protocol observers based
 *  on their SMS address type.
 *  A higher priority observer will be given the opportunity to
 *  accept a received messages before one of a lower priority.
 */
class TKeySmsObserver : public TKeyArrayFix
	{
public:
	TKeySmsObserver();
	virtual TInt Compare(TInt aLeft,TInt aRight) const;
	static TInt SmsAddressPriority(const TSmsAddr& aAddr);
	};


/**
 *  Constructor
 *  
 */
TKeySmsObserver::TKeySmsObserver()
	:TKeyArrayFix(0,ECmpTInt)
	{
	iKeyOffset = 0;
	} // TKeySmsObserver::TKeySmsObserver


/**
 *  Static method that returns the priority of a given SMS address.
 *  
 *  @leave Panics on an unknown address type.
 *  
 */
TInt TKeySmsObserver::SmsAddressPriority(const TSmsAddr& aAddr)
	{
	TSmsAddrFamily fam = aAddr.SmsAddrFamily();

	switch (fam)
		{
		case ESmsAddrEmail:
			return 11;
		case ESmsAddrApplication8BitPort:
			return 10;
		case ESmsAddrApplication16BitPort:
			return 9;
		case ESmsAddrMessageIndication:
			return 8;
		case ESmsAddrStatusReport:
			return 7;
		case ESmsAddrMatchIEI:
			return 6;
		case ESmsAddrMatchText:
			return 5;
		case ESmsAddrRecvAny:
			return 4;
		case ESmsAddrSendOnly:
			return 3;
		case ESmsAddrLocalOperation:
			return 2;
		case ESmsAddrUnbound:
			return 1;
		default:
			SmspPanic(ESmspUnknownSmsAddressFamily);
		};
	return KErrNotFound;
	} // TKeySmsObserver::SmsAddressPriority


/**
 *  Compares two observers.
 *  Override of TKeyArrayFix::Compare() called on to help sort
 *  the observer list based on their address types.
 *  
 */
TInt TKeySmsObserver::Compare(TInt aLeft, TInt aRight) const
	{
	LOGSMSPROT3("TKeySmsObserver::Compare [left=%d, right=%d]", aLeft, aRight);

	const TInt lhptr = -1; // Left higher priority than right
	const TInt rhptl = 1; // Right higher priority than left

	MSmsMessageObserver* left = *(MSmsMessageObserver**)At(aLeft);
	MSmsMessageObserver* right = *(MSmsMessageObserver**)At(aRight);

	const TSmsAddr& leftAddr = left->GetLocalAddress();
	const TSmsAddr& rightAddr = right->GetLocalAddress();

	TSmsAddrFamily leftFamily = leftAddr.SmsAddrFamily();


	TInt leftPriority = SmsAddressPriority(leftAddr);
	TInt rightPriority = SmsAddressPriority(rightAddr);

	if (leftPriority > rightPriority)
		return lhptr;
	if (rightPriority > leftPriority)
		return rhptl;

	if (leftFamily == ESmsAddrMatchText)
		{
		// Longer strings are higher priority than shorter ones
		TInt rightLen = rightAddr.TextMatch().Length();
		TInt leftLen = leftAddr.TextMatch().Length();
		if (leftLen > rightLen)
			return lhptr;
		if (rightLen > leftLen)
			return rhptl;
		}

	return 0;
	} // TKeyArrayFix::Compare


/**
 *  Re-orders the observer list using TKeySmsObserver to determine priorities.
 *  
 */
void CSmsProtocol::OrderSmsMessageObserver(const MSmsMessageObserver& /*aObserver*/)
	{
	LOGSMSPROT1("CSmsProtocol::OrderSmsMessageObserver()");

	TKeySmsObserver smsObsKey;
#ifdef _DEBUG
	TInt ret=iSmsMessageObserverList.Sort(smsObsKey);
	__ASSERT_DEBUG(ret==KErrNone,SmspPanic(ESmspCorruptObserverList));
#else
    iSmsMessageObserverList.Sort(smsObsKey);
#endif
	} // CSmsProtocol::OrderSmsMessageObserver


/**
 *  Attempts to match an incoming message to a SAP / observer.  Starts with the
 *  highest priority observer.
 *  
 */
MSmsMessageObserver* CSmsProtocol::MatchSmsMessageToObserver(const CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT1("CSmsProtocol::MatchSmsMessageToObserver()");

	TInt count=iSmsMessageObserverList.Count();
	for (TInt i=0;i<count;i++)
		{
		MSmsMessageObserver* obs = iSmsMessageObserverList[i];
		if (IsMatch(obs->GetLocalAddress(),aSmsMessage))
			return obs;
		}
	return NULL;
	} // CSmsProtocol::MatchSmsMessageToObserver


/**
 *  Tries to match an SMS address to a SAP / observer.  Starts with the highest
 *  priority observer.
 *  
 */
MSmsMessageObserver* CSmsProtocol::MatchSmsAddressToObserver(const TSmsAddr& aAddr)
	{
	LOGSMSPROT1("CSmsProtocol::MatchSmsAddressToObserver()");

	TInt count=iSmsMessageObserverList.Count();
	for (TInt i=0;i<count;i++)
		{
		MSmsMessageObserver* obs = iSmsMessageObserverList[i];
		if (obs->GetLocalAddress()==aAddr)
			return obs;
		}
	return NULL;
	} // CSmsProtocol::MatchSmsAddressToObserver


/**
 *  Static function checks if a message is matched to a given SMS address type.	Used by
 *  MatchSmsMessageToObserver() to find a matching observer for the message.
 *  
 */
TBool CSmsProtocol::IsMatch(const TSmsAddr& aSmsAddr, const CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT1("CSmsProtocol::IsMatch()");

	TSmsAddrFamily family = aSmsAddr.SmsAddrFamily();

	switch(family)
	{
		case (ESmsAddrUnbound):
		case (ESmsAddrSendOnly):
		case(ESmsAddrLocalOperation):
			return EFalse;

		case(ESmsAddrApplication8BitPort ):
			{
			return (MatchApplicationPort(aSmsMessage,aSmsAddr.Port(),EFalse));
			}

		case (ESmsAddrApplication16BitPort ):
			{
			return(MatchApplicationPort(aSmsMessage,aSmsAddr.Port(),ETrue));
			}

		case (ESmsAddrMessageIndication):
			{
			if (MatchInformationElement(aSmsMessage,CSmsInformationElement::ESmsIEISpecialSMSMessageIndication))
				return ETrue;
			if (aSmsMessage.SmsPDU().DataCodingSchemePresent())
				{
				TSmsDataCodingScheme::TSmsDCSBits7To4 bits7to4 = aSmsMessage.SmsPDU().Bits7To4();
				switch (bits7to4)
					{
					case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationDiscardMessage:
					case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndication7Bit:
					case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationUCS2:
						return ETrue;
					default:
						break;
					}
				}
			// CPHS Implementation of message waiting indicator (CPHS Phase 2)
			TGsmSmsTelNumber address;
			aSmsMessage.ParsedToFromAddress(address);

			//as far as I can see you can't get the npi from the address. (==0?)
			//might want to compare the content of the tel number with the spec to make sure
			//that we are dealing with a cphs message !

			if((address.iTypeOfAddress.TON() == EGsmSmsTONAlphaNumeric)  &&
				(address.iTelNumber.Length() == 4) &&
					((address.iTelNumber[2] & 0x7E) == 0x10)  &&// x001 000x constant value
						((address.iTelNumber[3] & 0x7E) == 0x00) )// x000 000x constant value
						return ETrue; // This means that the SMS received is a Message Waiting indication
			return EFalse;
			}

		case (ESmsAddrStatusReport):
			{
			LOGSMSPROT1("TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationDiscardMessage:");
			if (aSmsMessage.Type() == CSmsPDU::ESmsStatusReport)
				return ETrue;
			}
			break;
		case (ESmsAddrMatchIEI):
			{
			return (MatchInformationElement(aSmsMessage,CSmsInformationElement::TSmsInformationElementIdentifier(aSmsAddr.IdentifierMatch())));
			}
		case (ESmsAddrMatchText):
			{
			TBuf<TSmsAddr::EMaxTextMatchLength> textMatch;
			textMatch.Copy(aSmsAddr.TextMatch());
			TBuf<TSmsAddr::EMaxTextMatchLength> smsMsgBuf;
			TInt bufferLen = aSmsMessage.Buffer().Length();
			bufferLen = Min(bufferLen,textMatch.Length());
			aSmsMessage.Buffer().Extract(smsMsgBuf,0,bufferLen);
			if (textMatch.CompareF(smsMsgBuf)==KErrNone)
				return ETrue;
			return EFalse;
			}
		case (ESmsAddrEmail):
			{
			if(aSmsMessage.IsEmailHeader())
	 			return ETrue;
			break;
			}
		case (ESmsAddrRecvAny):
			return ETrue;
		default:
			return EFalse;

		}
	return EFalse;
	} // CSmsProtocol::IsMatch

/**
 *  Static function checks if a message contains a given port.  Used by IsMatch()
 *  to determine if an SMS address type matches an application port.
 *  
 */
TBool CSmsProtocol::MatchApplicationPort(const CSmsMessage& aSmsMessage,TUint aPort,TBool a16Bit)
	{
	LOGSMSPROT1("CSmsProtocol::MatchApplicationPort");

	if (!aSmsMessage.SmsPDU().UserDataPresent())
		return EFalse;

	const CSmsPDU& Pdu = aSmsMessage.SmsPDU();
	TInt ToPort = 0;
	TInt FromPort = 0;
	TBool Is16BitPorts = 0;

    Pdu.ApplicationPortAddressing(ToPort,FromPort,&Is16BitPorts);
	if((TInt)aPort == ToPort && Is16BitPorts == a16Bit) return ETrue;

	return EFalse;
	} // CSmsProtocol::MatchApplicationPort


/**
 *  Static function checks if a message contains a given information element.  Used by IsMatch()
 *  to determine if an SMS address type matches a message.
 *  
 */
TBool CSmsProtocol::MatchInformationElement(const CSmsMessage& aSmsMessage, CSmsInformationElement::TSmsInformationElementIdentifier aIeVal)
	{
	LOGSMSPROT1("CSmsProtocol::MatchInformationElement");

	if (!aSmsMessage.SmsPDU().UserDataPresent())
		return EFalse;

	TInt count=aSmsMessage.SmsPDU().UserData().NumInformationElements();
	for (TInt i=0; i<count; i++)
		{
		TInt identifier=aSmsMessage.SmsPDU().UserData().InformationElement(i).Identifier();
		if (identifier==aIeVal)
			return ETrue;
		}
	return EFalse;
	} // CSmsProtocol::MatchInformationElement


/**
 *  Searches the reassembly store for complete messages and tries to match
 *  them to an observer.  If a match is found the message is passed to
 *  the observer and deleted from the store, otherwise it's left in store.
 *  Called when a new observer is added or a PDU has been received and
 *  successfully processed.
 *  
 */
void CSmsProtocol::ProcessCompleteSmsMessagesL()
	{
	LOGSMSPROT1("CSmsProtocol::ProcessCompleteSmsMessagesL");

	if(iPhoneEnumerationObserver) return;

	iReassemblyStore->ProcessCompleteSmsMessagesL(*this, iSmsPDURead->CurrentMessage());
	} // CSmsProtocol::ProcessCompleteSmsMessagesL


/**
 *  Called when the state machine for processing a received PDU has completed.
 *  The reassembly store access count is incremented and purged if it exceeds
 *  CSmsProtocol::KNumSARStoreAccessesBeforePurging.
 *  The completed entry is removed from the processing queue and if the queue is
 *  not empty, and the processing state machinery is started for the next entry.
 *  
 */
void CSmsProtocol::MessageReadedSuccessfully()
	{
	LOGSMSPROT1("CSmsProtocol::MessageReadedSuccessfully");
	TRAPD(ret,ProcessCompleteSmsMessagesL());
	if(ret!=KErrNone)
		{
		LOGSMSPROT2("WARNING! CSmsProtocol::ProcessCompleteSmsMessagesL left with %d", ret);
		}

	iNumReassemblyStoreAccesses++;
	if(iNumReassemblyStoreAccesses>=KNumSARStoreAccessesBeforePurging)
		{
		TRAP(ret, iReassemblyStore->PurgeL(iSmsSettings.ReassemblyLifetime(),EFalse));

		if(ret!=KErrNone)
			{
			LOGSMSPROT2("WARNING! iReassemblyStore->PurgeL left with %d", ret);
			}

		iNumReassemblyStoreAccesses=0;
		}
	if(CheckPoweredUp() == KErrNone )
		iSmsPDURead->ResumeSmsReception();
	} // CSmsProtocol::MessageReadedSuccessfully


/**
 *  method to delete sms from the reastore
 */
void CSmsProtocol::DeleteSMSFromReaStoreL(const CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT1("CSmsProtocol::DeleteSMSFromReaStoreL entry");
	if (aSmsMessage.Type() == CSmsPDU::ESmsStatusReport)
	{
		LOGSMSPROT1("CSmsProtocol::DeleteSMSFromReaStoreL it's SR");
		return;
	}

	TSmsDataCodingScheme::TSmsClass  msgClass;

	if (aSmsMessage.SmsPDU().DataCodingSchemePresent()	&&	aSmsMessage.SmsPDU().Class(msgClass))
		{
		if (msgClass == TSmsDataCodingScheme::ESmsClass0)
			{
			if (!aSmsMessage.IsComplete())
				{
				CIncompleteClass0MessageInfo& incompleteClass0MsgInfo = (CIncompleteClass0MessageInfo &) aSmsMessage.GetOperationsForNonIEL(ESmsIncompleteClass0MessageParameter);
				// Retrieve incomplete class 0 message information & process
				TInt startPos, endPos;
				TBool isLastMessage;
				incompleteClass0MsgInfo.GetIncompleteMessageInfoL(startPos, endPos, isLastMessage);
				if (!isLastMessage)
					{
					/*
					Only in this condition set incomplete message as forwarded
					which internally will remove the PDUs from pre-allocated file.
					*/
					LOGSMSPROT1("CSmsProtocol::DeleteSMSFromReaStoreL Incomplete Message Not last segment");
					iReassemblyStore->SetIncompleteMessageForwardedToClientL(aSmsMessage);
					return;
					}
				}
			}
		}
	iReassemblyStore->DeleteMessageL(aSmsMessage, ETrue);

	LOGSMSPROT1("CSmsProtocol::DeleteSMSFromReaStoreL exit");
	} // CSmsProtocol::DeleteSMSFromReaStoreL


/**
 *  Used to notify observer of the change in state of the modem connection.
 *  Send only observers are not notified (why?).
 */
void CSmsProtocol::NotifyMessageObservers(TInt aStatus)
	{
	LOGSMSPROT1("CSmsProtocol::NotifyMessageObservers");

	TInt count=iSmsMessageObserverList.Count();
	LOGSMSPROT3("CSmsProtocol::NotifyMessageObservers [count=%d, aStatus=%d]",count, aStatus);
	for (TInt index=0; index<count; index++)
		{
		MSmsMessageObserver* observer=iSmsMessageObserverList[index];
		TSmsAddrFamily fam = observer->GetLocalAddress().SmsAddrFamily();
	    LOGSMSPROT2("CSmsProtocol::NotifyMessageObservers [family=%d]", fam);
		switch (fam)
			{
			case ESmsAddrMessageIndication:
			case ESmsAddrStatusReport:
			case ESmsAddrMatchIEI:
			case ESmsAddrMatchText:
			case ESmsAddrEmail:
			case ESmsAddrRecvAny:
			case ESmsAddrLocalOperation:
			case ESmsAddrApplication8BitPort:
	        case ESmsAddrApplication16BitPort:
				{
				observer->ModemNotificationCompleted(aStatus);
				break;
				}
			case ESmsAddrSendOnly:
			case ESmsAddrUnbound:
				break;
			default:
				SmspPanic(ESmspPanicAddrFamilyNotAllowed);
			}
		}
	} // CSmsProtocol::NotifyMessageObservers


void CSmsProtocol::PowerUp()
	{
	LOGSMSPROT2("CSmsProtocol::PowerUp [iState=%d]", iState);
	__ASSERT_DEBUG(iState == EPoweredDown, SmspPanic(KSmspPanicUnexpectedState));

	if (iState == EPoweredDown)
		{
		TRAPD(err, DoPowerUpL());

		if (err != KErrNone)
			{
			LOGSMSPROT3("WARNING: CSmsProtocol::DoPowerUpL left [err=%d iState=%d]", err, iState);
			__ASSERT_DEBUG(iState == EPoweredDown, SmspPanic(KSmspPanicUnexpectedState));
			PowerDown();
			}
		}
	} // CSmsProtocol::PowerUp


/**
 *  Handles the event of the modem powering on (when it had been powered off).
 *  Does the following...
 *  - The TSY loaded
 *  - ETEL resource handles initialised
 *  - ETEL parameters set and retrieved
 *  - A number of state machines started
 *  
 */
void CSmsProtocol::DoPowerUpL()
	{
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL");
	__ASSERT_DEBUG(iModemDetection==RPhone::EDetectedPresent,SmspPanic(KSmspPhoneHasNotTurnedOn));

	//Open the segmentation and reassembly stores

	iReassemblyStore->OpenStoreL();
	iSegmentationStore->OpenStoreL();
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL->PurgeL Start");
	iReassemblyStore->PurgeL(iSmsSettings.ReassemblyLifetime(), ETrue);
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL->PurgeL End");
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL->PurgeL Start");
	iSegmentationStore->PurgeL(iSmsSettings.KSegmentationLifetimeMultiplier(),EFalse);
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL->PurgeL End");

	// Connect to ETEL and load the TSY
	User::LeaveIfError(iTelServer.Connect());
	User::LeaveIfError(iTelServer.LoadPhoneModule(iGsmTsyName));
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL  Connected to Etel");

	// Find the phone corresponding to this TSY and open a number of handles on it
	TInt numPhones;
	User::LeaveIfError(iTelServer.EnumeratePhones(numPhones));
	TInt i=0;
	for (; i<numPhones; i++)
		{
		RTelServer::TPhoneInfo info;
		User::LeaveIfError(iTelServer.GetPhoneInfo(i,info));
		TName tsyName;
		User::LeaveIfError(iTelServer.GetTsyName(i,tsyName));
		if (tsyName.CompareF(iGsmTsyName)==KErrNone)
			{
			User::LeaveIfError(iGsmPhone.Open(iTelServer,info.iName));
			User::LeaveIfError(iEnumerationPhone.Open(iTelServer,info.iName));
			User::LeaveIfError(iWritePhone.Open(iTelServer,info.iName));
			break;
			}
		}
	__ASSERT_DEBUG(i<numPhones,SmspPanic(ESmspPhoneNotFound));
	if (iTelServer.SetExtendedErrorGranularity(RTelServer::EErrorExtended)!=KErrNone)
		User::LeaveIfError(iTelServer.SetExtendedErrorGranularity(RTelServer::EErrorBasic));
	User::LeaveIfError(iSmsMessaging.Open(iGsmPhone));

	LOGSMSPROT1("CSmsProtocol::DoPowerUpL  Opened TSY handles");

	if (iSmsPhoneEnumeration == NULL)
		iSmsPhoneEnumeration=CSmsPhoneEnumeration::NewL(*this, iSmsSettings, *iReassemblyStore, *iSegmentationStore, iEnumerationPhone, KSmsSessionPriority, *iSmsMonitorDiskSpace);

	if (iWriteQueue == NULL)
		iWriteQueue = CSmspWriteQueue::NewL(*this, iSmsSettings, iWritePhone,*iSegmentationStore, KSmsSessionPriority);

	if (iDeleteQueue == NULL)
		iDeleteQueue = CSmspDeleteQueue::NewL(*this,iSmsSettings,iSmsMessaging, KSmsSessionPriority);

	// Start state machines
	iSmsPhoneInitialization->Start();

	LOGSMSPROT1("CSmsProtocol::DoPowerUpL  Started state machines");

	NotifyMessageObservers(KIoctlSelectModemPresent);
	LOGSMSPROT1("CSmsProtocol::DoPowerUpL  Notified message observers");


	// Process any waiting messages
	ProcessCompleteSmsMessagesL();

	iState = EPoweredUp;
	} // CSmsProtocol::DoPowerUpL


/**
 *  Handles the event of the modem powering off (when it had been powered on).
 *  Does the following...
 *  - State mechines cancelled
 *  - Close TSY handles
 *  - Disconnect from Etel
 *  
 *  This function will also be called if PowerUpL() leaves
 */
void CSmsProtocol::PowerDown()
    {
    LOGSMSPROT1("CSmsProtocol::PowerDown");
    
    iSetBearer->Cancel();
    iReceiveMode->Cancel();
    
    iSendQueue->Cancel();
    
    iSmsPDURead->Cancel();
    
    iSmsMonitorDiskSpace->Cancel();
    
    delete iSmsPhoneEnumeration; //must be deleted because uses iSmsMessaging which is soon closed
    iSmsPhoneEnumeration = NULL;
    
    iSmsPhoneInitialization->Cancel();
    
    if( iSmsReadParams != NULL )
        {
        iSmsReadParams->Cancel();
        }
    if( iSmsWriteParams != NULL )
        {
        iSmsWriteParams->Cancel();
        }
    
    delete iWriteQueue; //must be deleted because uses iSmsMessaging which is soon closed
    iWriteQueue = NULL;
    
    delete iDeleteQueue; //must be deleted because uses iSmsMessaging which is soon closed
    iDeleteQueue = NULL;
    
    LOGSMSPROT1("CSmsProtocol::PowerDown  Cancelled state machines");
    
    NotifyMessageObservers(KIoctlSelectModemNotPresent);
    LOGSMSPROT1("CSmsProtocol::PowerDown  Notified message observers");
    
    // Close TSY handles
    iSmsMessaging.Close();
    iGsmPhone.Close();
    iEnumerationPhone.Close();
    iWritePhone.Close();
    LOGSMSPROT1("CSmsProtocol::PowerDown  Closed TSY handles");
    
    // Disconnect from Etel
    iTelServer.Close();
    LOGSMSPROT1("CSmsProtocol::PowerDown  Disconnected from Etel");
    
    iReassemblyStore->Close();
    iSegmentationStore->Close();
    
    iState = EPoweredDown;
    }

void CSmsProtocol::CloseNowWrap()
    {
    // Ignore in code coverage - called when PDU delete queue is not 
    // empty and SMS stack is closing down - can only get this situation
    // when the PDU delete has been initiated by the SMS stack itself 
    // (rather than the client) and the PDUs are still being deleted 
    // when last client disconnects.
    BULLSEYE_OFF
    LOGSMSPROT1("CSmsProtocol::CloseNowWrap()");
    
    if( iDeleteQueue == NULL || !iDeleteQueue->IsActive() )
        {
        CloseNow();
        }
    BULLSEYE_RESTORE
    }

void CSmsProtocol::HandleBackupOrRestoreStartingL()
    {
    LOGSMSPROT2("CSmsProtocol::HandleBackupOrRestoreStartingL [ModemState=%d]",  iSmsModemNotification->ModemState());
    PowerDown();
    }

void CSmsProtocol::HandleBackupOrRestoreCompleteL()
    {
    LOGSMSPROT2("CSmsProtocol::HandleBackupOrRestoreCompleteL [ModemState=%d]", iSmsModemNotification->ModemState());
    
    if (iModemDetection == RPhone::EDetectedPresent)
        {
        PowerUp();
        }
    }

/**
 *  Set the sap port number
 */
TBool CSmsProtocol::AllocateLocalAddress(TSmsAddr& aAddr)
	{
    LOGSMSPROT1("CSmsProtocol::AllocateLocalAddressL");

	TBool found=EFalse;
	TUint count=0,attempts=0;
	TSmsAddr locAddr=aAddr;

	if(locAddr.SmsAddrFamily() == ESmsAddrApplication8BitPort )
		{
		count =KMax8BitPortNumber-KMin8BitPortNumber+1;
		}
	else if(locAddr.SmsAddrFamily() == ESmsAddrApplication16BitPort )
		{
		count =KMax16BitPortNumber-KMin16BitPortNumber+1;
		}

	for(;!found && attempts < count; ++attempts)
		{
		if(locAddr.SmsAddrFamily() == ESmsAddrApplication8BitPort )
			{
			locAddr.SetPort(iNext8BitPort++);
			if(iNext8BitPort > KMax8BitPortNumber)iNext8BitPort=KMin8BitPortNumber;
			}
		else if(locAddr.SmsAddrFamily() == ESmsAddrApplication16BitPort )
			{
			locAddr.SetPort(iNext16BitPort++);
			if(iNext16BitPort > KMax16BitPortNumber)iNext8BitPort=KMin16BitPortNumber;
			}
		if(!SmsAddrIsAlreadyUsed(NULL,locAddr))found=ETrue;
		}

	if(found)
		{
		aAddr=locAddr;
		}
	return found;
	} // CSmsProtocol::AllocateLocalAddress

/**
 *  Static method that returns the boolean value indicating whether the given
 *  CSmsMessage is for application port.
 *  This function is an utility function which determines the passed sms message
 *  is for application port or not. This function is also used in CFacadeSmsReassemblyStore class.
 *
 *  @param aSmsMessage reference to CSmsMessage object.
 *  
 */
TBool CSmsProtocol::IsAppPortSMS(const CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT1("CSmsProtocol::IsAppPortSMS()");

	TSmsAddr addr;
	addr.SetSmsAddrFamily(ESmsAddrMatchIEI);
	addr.SetIdentifierMatch(CSmsInformationElement::ESmsIEIApplicationPortAddressing8Bit);
	if (IsMatch(addr,aSmsMessage))
		{
		return ETrue;
		}

	addr.SetSmsAddrFamily(ESmsAddrMatchIEI);
	addr.SetIdentifierMatch(CSmsInformationElement::ESmsIEIApplicationPortAddressing16Bit);
	if (IsMatch(addr,aSmsMessage))
		{
		return ETrue;
		}

	addr.SetSmsAddrFamily(ESmsAddrMatchText);
	addr.SetTextMatch(_L8("//SCK"));
	if (IsMatch(addr,aSmsMessage))
		{
		return ETrue;
		}

	return EFalse;
	} // CSmsProtocol::IsAppPortSMS


const RMobilePhone::TMobilePhoneNetworkInfoV1& CSmsProtocol::NetworkInfo() const
	{
	LOGSMSPROT1("CSmsProtocol::NetworkInfo()");

	return iSmsPhoneInitialization->NetworkInfo();
	} // CSmsProtocol::NetworkInfo


TBool CSmsProtocol::NetworkInfoAvailable() const
	{
	LOGSMSPROT1("CSmsProtocol::NetworkInfoAvailable()");

	return iSmsPhoneInitialization->NetworkInfoAvailable();
	} // CSmsProtocol::NetworkInfoAvailable


// implementation of CSmsProtocolBootTimer
//

/**
 *  2 phase contructor
 *  
 *  @param aActive Reference to an CSmsProtocol object
 */
CSmsProtocolBootTimer* CSmsProtocolBootTimer::NewL(CSmsProtocol& aSmsProtocol)
    {
    LOGSMSPROT1("CSmsProtocolBootTimer::NewL");
    
    CSmsProtocolBootTimer* self = new(ELeave) CSmsProtocolBootTimer(aSmsProtocol);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/**
 *  D'tor
 */
CSmsProtocolBootTimer::~CSmsProtocolBootTimer()
    {
    Cancel();
    }

/**
 *  Start a timeout specified in aTimeIntervalMicroSeconds32
 */
void CSmsProtocolBootTimer::Start(const TTimeIntervalMicroSeconds32& aTimeIntervalMicroSeconds32)
	{
	LOGSMSPROT1("CSmsProtocolBootTimer::Start");
	After(aTimeIntervalMicroSeconds32);
	}

/**
 *  C'tor
 */
CSmsProtocolBootTimer::CSmsProtocolBootTimer(CSmsProtocol& aSmsProtocol)
: CTimer(KSmsSessionPriority), iSmsProtocol(aSmsProtocol)
    {
    CActiveScheduler::Add(this);
    }

/**
 *  Timer completed - cancel the observer
 */
void CSmsProtocolBootTimer::RunL()
    {
    LOGSMSPROT2("CSmsProtocolBootTimer::RunL [iStatus=%d]", iStatus.Int() );
    iSmsProtocol.MessageReadedSuccessfully();
    }
