// 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();
}