// Copyright (c) 1997-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:
// Implementation for the CSmsuActiveBase based class CSmsMessageSend.
//
//
/**
@file
@internalAll
*/
#include "smspsend.h"
#include "smspstor.h"
#include "smspmain.h"
#include "smsuset.h"
#include "smsulog.h"
#include "exterror.h"
#include "gsmubuf.h"
#include "SmsuTimer.h"
#include <logwraplimits.h>
/**
* Static object constructor for CSmsMessageSend.
*
* @param aSegmentationStore Segmentation Store.
* @param aSmsMessaging ETel SMS messaging subsession.
* @param aSmsSettings The global SMS settings.
* @param aMobileSmsCaps SMS messaging capabilities of TSY/phone.
* @param aPriority Priority to use for the Active Object.
* @param aSmspSetBearer Set bearer object.
*/
CSmsMessageSend* CSmsMessageSend::NewL(CSmsSegmentationStore& aSegmentationStore,
const TSmsSettings& aSmsSettings,
const RMobileSmsMessaging::TMobileSmsCapsV1& aMobileSmsCaps,
RMobileSmsMessaging& aSmsMessaging,
TInt aPriority,
CSmspSetBearer& aSmspSetBearer)
{
LOGSMSPROT1("CSmsMessageSend::NewL()");
CSmsMessageSend* self = new (ELeave) CSmsMessageSend(aSegmentationStore,
aSmsSettings,
aMobileSmsCaps,
aSmsMessaging,
aPriority,
aSmspSetBearer);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
} // CSmsMessageSend::NewL
/**
* 2nd Phase of construction.
*
* Creates CSmsTimeout object and the SMS event logger.
*/
void CSmsMessageSend::ConstructL()
{
LOGSMSPROT1("CSmsMessageSend::ConstructL()");
iSmsEventLogger = CSmsEventLogger::NewL(iSegmentationStore.FileSession(),
Priority());
ConstructTimeoutL();
} // CSmsMessageSend::ConstructL
/**
* Default constructor.
*
* @param aSegmentationStore Segmentation Store.
* @param aSmsSettings The global SMS settings.
* @param aMobileSmsCaps SMS messaging capabilities of TSY/phone.
* @param aSmsMessaging ETel SMS messaging subsession.
* @param aPriority Priority to use for the Active Object.
* @param aSmspSetBearer Set bearer object.
*/
CSmsMessageSend::CSmsMessageSend(CSmsSegmentationStore& aSegmentationStore,
const TSmsSettings& aSmsSettings,
const RMobileSmsMessaging::TMobileSmsCapsV1& aMobileSmsCaps,
RMobileSmsMessaging& aSmsMessaging,
TInt aPriority,
CSmspSetBearer& aSmspSetBearer)
: CSmsuActiveBase(aPriority),
iState(ESmsMessageSendIdle),
iSegmentationStore(aSegmentationStore),
iSmspSetBearer(aSmspSetBearer),
iSmsSettings(aSmsSettings),
iMobileSmsCaps(aMobileSmsCaps),
iSmsMessaging(aSmsMessaging),
iMobileSmsSendAttributesV1Pckg(iMobileSmsSendAttributesV1),
iSmsArray(8),
iSegmentSequenceNumber(-1)
{
// NOP
} // CSmsMessageSend::CSmsMessageSend
/**
* Standard destructor.
*/
CSmsMessageSend::~CSmsMessageSend()
{
Cancel();
delete iSmsEventLogger;
} // CSmsMessageSend::~CSmsMessageSend
void CSmsMessageSend::Start(CSmsMessage& aSmsMessage, TInt aOptions,
const TSmsAddr& aSmsAddr, TRequestStatus& aStatus)
{
LOGSMSPROT1("CSmsMessageSend::Start()");
__ASSERT_DEBUG(iState == ESmsMessageSendIdle,SmspPanic(KSmspPanicUnexpectedState));
//
// Store the request status that we will complete when finished...
//
Queue(aStatus);
//
// Initialise member data...
//
iSmsMessage=&aSmsMessage;
iSmsMessage->ParsedToFromAddress(iToFromTelNumber);
iOptions = aOptions;
iSmsAddr = aSmsAddr;
iSmsPDUData.iType = aSmsMessage.Type();
iSmsPDUData.iTotal = 0;
iSmsPDUData.iSent = 0;
iSmsPDUData.iDelivered = 0;
iSendError = KErrNone;
iSmsEventLogger->Event().SetId(KLogNullId);
//
// Begin by setting the bearer...
//
iState = ESmsMessageSendSetBearer;
iSmspSetBearer.NotifyBearerSet(iStatus);
SetActive();
} // CSmsMessageSend::Start
void CSmsMessageSend::DoRunL()
{
LOGSMSPROT3("CSmsMessageSend::RunL(): iStatus=%d, iState=%d", iStatus.Int(), iState);
switch (iState)
{
case ESmsMessageSendSetBearer:
{
//
// If setting of bearer was successful then send the message.
//
// The TSY will complete with KErrNotSupported if it doesn't
// support setting the send bearer.
//
if (iStatus.Int() == KErrNone ||
iStatus.Int() == KErrNotSupported)
{
if (iSmsEventLogger->ClientAvailable())
{
CreateLogServerEvent();
}
else
{
SegmentMessage();
}
}
}
break;
case ESmsMessageSendCreatingLogServerEvent:
{
//
// If a log event was created ok, then store the id...
//
if (iStatus.Int() == KErrNone)
{
iSmsMessage->SetLogServerId(iSmsEventLogger->Event().Id());
}
else
{
iSmsEventLogger->Event().SetId(KLogNullId);
}
//
// Segment the message. This will send the first PDU as well...
//
SegmentMessage();
}
break;
case ESmsMessageSendSegmentingMessage:
{
//
// If the previous PDU was sent ok, then send the next one...
//
if (iStatus.Int() == KErrNone)
{
SendNextPDU();
}
else
{
//
// An error occured. Save the error for later and either
// update the log event, or drop to the end of RunL() so
// that Complete() is called...
//
iSendError = iStatus.Int();
if (iSmsEventLogger->Event().Id() != KLogNullId)
{
UpdateLogServerEvent();
}
}
}
break;
case ESmsMessageSendPDU:
{
//
// If the PDU is sent with an acknowledgement, then decode the
// submit report...
//
if (iMobileSmsCaps.iSmsControl & RMobileSmsMessaging::KCapsSendWithAck &&
iMobileSmsSendAttributesV1.iFlags & RMobileSmsMessaging::KGsmSubmitReport)
{
DecodeSubmitReportL();
}
if (iStatus.Int() == KErrNone && !IsRPError())
{
LOGSMSIF2("CSmsMessageSend::RunL(): Submit ACK'd MsgRef=%d",
iMobileSmsSendAttributesV1.iMsgRef);
}
else if (IsRPError())
{
LOGSMSIF2("CSmsMessageSend::RunL(): Submit N'ACK'D MsgRef=%d",
iMobileSmsSendAttributesV1.iMsgRef);
}
else
{
LOGSMSIF2("CSmsMessageSend::RunL(): MsgRef=%d",
iMobileSmsSendAttributesV1.iMsgRef);
}
//
// If the PDU was sent okay, then update the segmentation store...
//
if (iStatus.Int() ==KErrNone)
{
iSegmentSequenceNumber++;
UpdateSegmentationStore();
}
else
{
//
// An error occured. Save the error for later and either
// update the log event, or drop to the end of RunL() so
// that Complete() is called...
//
iSendError = iStatus.Int();
if (iSmsEventLogger->Event().Id() != KLogNullId)
{
UpdateLogServerEvent();
}
}
}
break;
case ESmsMessageSendUpdatingSegmentationStore:
{
if (iStatus.Int() == KErrNone)
{
//
// The update has been successful, so update the log event or
// send the next PDU. If the log event is updated, then it
// will then send the next PDU...
//
if (iSmsEventLogger->Event().Id() != KLogNullId)
{
UpdateLogServerEvent();
}
else
{
SendNextPDU();
}
}
else
{
//
// An error occured. Save the error for later and either
// update the log event, or drop to the end of RunL() so
// that Complete() is called...
//
iSendError = iStatus.Int();
if (iSmsEventLogger->Event().Id() != KLogNullId)
{
UpdateLogServerEvent();
}
}
}
break;
case ESmsMessageSendUpdatingLogEvent:
{
//
// Ignore log server error and continue with the next PDU if
// the last send was okay, otherwise drop to the bottom of the
// RunL() and Complete().
//
if (iSendError == KErrNone)
{
SendNextPDU();
}
}
break;
default:
{
SmspPanic(KSmspPanicUnexpectedState);
}
break;
};
//
// DoRunL() will now return to CSmsuActiveBase which if the object
// is not active, will call Complete().
//
} // CSmsMessageSend::DoRunL
void CSmsMessageSend::DoCancel()
{
LOGSMSPROT2("CSmsMessageSend::DoCancel(): iState=%d", iState);
TimedSetActiveCancel();
switch (iState)
{
case ESmsMessageSendSetBearer:
{
iSmspSetBearer.Cancel();
}
break;
case ESmsMessageSendSegmentingMessage:
{
// NOP
}
break;
case ESmsMessageSendCreatingLogServerEvent:
case ESmsMessageSendUpdatingLogEvent:
{
iSmsEventLogger->Cancel();
}
break;
case ESmsMessageSendPDU:
{
iSmsMessaging.CancelAsyncRequest(EMobileSmsMessagingSendMessage);
}
break;
case ESmsMessageSendUpdatingSegmentationStore:
{
// NOP
}
break;
default:
{
SmspPanic(KSmspPanicUnexpectedState);
}
break;
};
iSendError = KErrCancel;
//
// Handle completion of this Active Object. Note that the object
// may well still be active at this point...
//
if (TimedOut())
{
Complete(KErrTimedOut);
}
else
{
Complete(KErrCancel);
}
} // CSmsMessageSend::DoCancel
/**
* Decodes SMS-SUBMIT-REPORT PDU and checks if it is SMS-SUBMIT-REPORT for
* RP-ERROR.
*
* @leave Leaves if CSmsBuffer::NewL or CSmsMessage::NewL leaves.
*
*/
void CSmsMessageSend::DecodeSubmitReportL()
{
LOGSMSPROT1("CSmsMessageSend::DecodeSubmitReportL()");
//
// Only try to decode the submit report if error is a RP-error, in which case there should be a valid PDU
//
if (IsRPError())
{
TGsmSms sms;
sms.SetPdu(iMobileSmsSendAttributesV1.iSubmitReport);
CSmsBuffer* buffer=CSmsBuffer::NewL();
// try to create a sms message from the submit report PDU (smsMessage takes ownership of the buffer)
CSmsMessage* smsMessage = CSmsMessage::NewL(iSegmentationStore.FileSession(),
sms,buffer,IsRPError(),ETrue);
CleanupStack::PushL(smsMessage);
if (smsMessage->SmsPDU().Type() == CSmsPDU::ESmsSubmitReport)
{
CSmsSubmitReport& submitReport =(CSmsSubmitReport&) smsMessage->SmsPDU();
TInt failureCause = submitReport.FailureCause();
if (failureCause != TSmsFailureCause::ESmsErrorFree)
{
iStatus = FailureCauseToError(failureCause);
}
}
else
{
LOGSMSPROT3("Invalid PDU Type = %d with iStatus = %d", smsMessage->SmsPDU().Type(), iStatus.Int() );
}
CleanupStack::PopAndDestroy(smsMessage);
}
} // CSmsMessageSend::DecodeSubmitReportL
/**
* Checks if TSY completed send message request with RP-error.
*
* @return boolean.
*/
TBool CSmsMessageSend::IsRPError() const
{
LOGSMSPROT1("CSmsMessageSend::IsRPError()");
TBool isRPError = EFalse;
switch (iStatus.Int())
{
case KErrGsmSMSUnassignedNumber:
case KErrGsmSMSOperatorDeterminedBarring:
case KErrGsmSMSCallBarred:
case KErrGsmSMSReserved:
case KErrGsmSMSNetworkFailure:
case KErrGsmSMSShortMessageTransferRejected:
case KErrGsmSMSMemoryCapacityExceeded:
case KErrGsmSMSDestinationOutOfOrder:
case KErrGsmSMSUnidentifiedSubscriber:
case KErrGsmSMSFacilityRejected:
case KErrGsmSMSUnknownSubscriber:
case KErrGsmSMSNetworkOutOfOrder:
case KErrGsmSMSTemporaryFailure:
case KErrGsmSMSCongestion:
case KErrGsmSMSResourcesUnavailable:
case KErrGsmSMSRequestedFacilityNotSubscribed:
case KErrGsmSMSRequestedFacilityNotImplemented:
case KErrGsmSMSInvalidShortMessageTransferReferenceValue:
case KErrGsmSMSUnspecifiedInvalidMessage:
case KErrGsmSMSInvalidMandatoryInformation:
case KErrGsmSMSNonExistentMessageType:
case KErrGsmSMSIncompatibleMessageWithSmsProtocolState:
case KErrGsmSMSInformationElementNotImplemented:
case KErrGsmSMSUnspecifiedProtocolError:
case KErrGsmSMSUnspecifiedInterworkingError:
{
isRPError = ETrue;
}
break;
default:
{
// NOP
}
break;
}
return isRPError;
} // CSmsMessageSend::IsRPError
void CSmsMessageSend::SegmentMessage()
{
LOGSMSPROT1("CSmsMessageSend::SegmentMessage()");
iState = ESmsMessageSendSegmentingMessage;
TRAPD(ret,DoSegmentMessageL());
CompleteMyself(ret);
} // CSmsMessageSend::SegmentMessage
void CSmsMessageSend::DoSegmentMessageL()
{
LOGSMSPROT1("CSmsMessageSend::DoSegmentMessageL()");
TInt reference=0;
if (iSmsMessage->Type()==CSmsPDU::ESmsCommand)
{
CSmsCommand& command=(CSmsCommand&) iSmsMessage->SmsPDU();
CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray referencearray;
CleanupClosePushL(referencearray);
if (!iSegmentationStore.AddCommandL(iSmsAddr,*iSmsMessage, referencearray))
{
User::Leave(KErrNotFound);
}
const TInt count=referencearray.Count();
CArrayFix<TGsmSms>* smsarray=new(ELeave) CArrayFixFlat<TGsmSms>(8);
CleanupStack::PushL(smsarray);
for (TInt i=0; i<count; i++)
{
command.SetMessageNumber(referencearray[i].Reference());
//TODO should reference be set to referencearray[i].iReference here?
iSmsMessage->EncodeMessagePDUsL(*smsarray, reference);
iSmsArray.AppendL(smsarray->At(0));
smsarray->Reset();
}
CleanupStack::PopAndDestroy(2); // smsarray, referencearray
}
else
{
TBool iSentBefore=EFalse;
TBool is16bit = EFalse;
TBool concatenationIEPresent= iSmsMessage->SmsPDU().TextConcatenated( &is16bit );
iSmsMessage->SmsPDU().SetTextConcatenatedL(EFalse,EFalse);
// Fix for INC039985;
// The cause was that resending a SMS message(single PDU, same LogId),
// caused an assertion fault; This did not happen with an SMS with multiple PDUs.
// The reason for that is that for the multiple PDU case the stack employes a caching mechanism
// built in - it checks which PDUs from an SMS message have already been sent and
// then sends only the ones which have not been sent. This is performed by using the LodId,
// which is used to uniquely identify an SMS message and the PDUs it consists of.
// If an SMS message consisting of multiple PDUs is resend by an application and it has been already
// sent sucessfully, the stack deletes the previous entry corresponding to the old LogId
// segmentation store and replaces it with the new one with a new logid.
// This mechanism has been extended for SMS messages consisting of single PDU.
if(!iSmsMessage->EncodeIntoSinglePDUL(iSmsArray))
{
if (concatenationIEPresent && is16bit)
{
iSmsMessage->Set16BitConcatenation(ETrue);
}
if(iSegmentationStore.HasEntryWithLogIdL(iSmsMessage->LogServerId(),reference,iSmsPDUData.iSent))
{
iSentBefore=ETrue;
}
else
{
reference=is16bit? iSegmentationStore.Next16BitReferenceL(): iSegmentationStore.Next8BitReferenceL();
}
iSmsMessage->EncodeMessagePDUsL(iSmsArray, reference);
}
else
{
if( iSegmentationStore.HasEntryWithLogIdL(iSmsMessage->LogServerId(),reference,iSmsPDUData.iSent))
{
iSentBefore=ETrue;
}
}
LOGSMSPROT3("CSmsMesageSend::DoSegmentMessageL [LogServerId=%d][iSentBefore=%d]",iSmsMessage->LogServerId(),iSentBefore);
LOGSMSPROT3("CSmsMesageSend::DoSegmentMessageL [referenceNo=%d] [iCountOfSentBefore=%d ]",reference,iSmsPDUData.iSent);
if(!iSentBefore)
{
iSegmentationStore.AddSubmitL(iSmsAddr,*iSmsMessage);
}
}
} // CSmsMessageSend::DoSegmentMessageL
void CSmsMessageSend::CreateLogServerEvent()
{
LOGSMSPROT1("CSmsMessageSend::CreateLogServerEvent()");
iState=ESmsMessageSendCreatingLogServerEvent;
TLogId logid=(TLogId) iSmsMessage->LogServerId();
if (logid == KLogNullId)
{
iSmsPDUData.iTotal=iSmsArray.Count();
iSmsEventLogger->AddEvent(iStatus,*iSmsMessage,iSmsPDUData);
}
else
{
iSmsEventLogger->GetEvent(iStatus,logid);
}
SetActive();
} // CSmsMessageSend::CreateLogServerEvent
void CSmsMessageSend::SendNextPDU()
{
LOGSMSPROT3("CSmsMessageSend::SendNextPDU [sending pdu %d of count=%d]",iSmsPDUData.iSent, iSmsArray.Count() );
if (iSmsPDUData.iSent<iSmsArray.Count())
{
NMobilePhone::TMobileTON ton;
NMobilePhone::TMobileNPI npi;
TGsmSmsTelNumber parsedAddress(iSmsArray[iSmsPDUData.iSent].Sca());
iMobileSmsSendAttributesV1.iFlags = RMobileSmsMessaging::KGsmServiceCentre |
RMobileSmsMessaging::KRemotePartyInfo |
RMobileSmsMessaging::KSmsDataFormat |
RMobileSmsMessaging::KMoreToSend;
//
// Set service centre address...
//
parsedAddress.iTypeOfAddress.ConvertToETelMM(ton, npi);
iMobileSmsSendAttributesV1.iGsmServiceCentre.iTelNumber = parsedAddress.iTelNumber;
iMobileSmsSendAttributesV1.iGsmServiceCentre.iTypeOfNumber = static_cast<RMobilePhone::TMobileTON>(static_cast<TInt>(ton));
iMobileSmsSendAttributesV1.iGsmServiceCentre.iNumberPlan = static_cast<RMobilePhone::TMobileNPI>(static_cast<TInt>(npi));
//
// Set destination address...
//
iToFromTelNumber.iTypeOfAddress.ConvertToETelMM(ton, npi);
iMobileSmsSendAttributesV1.iDestination.iTelNumber = iToFromTelNumber.iTelNumber;
iMobileSmsSendAttributesV1.iDestination.iTypeOfNumber = static_cast<RMobilePhone::TMobileTON>(static_cast<TInt>(ton));
iMobileSmsSendAttributesV1.iDestination.iNumberPlan = static_cast<RMobilePhone::TMobileNPI>(static_cast<TInt>(npi));
iMobileSmsSendAttributesV1.iDataFormat = RMobileSmsMessaging::EFormatGsmTpdu;
if (iSmsPDUData.iSent < iSmsArray.Count() - 1)
{
iMobileSmsSendAttributesV1.iMore = ETrue;
}
else
{
iMobileSmsSendAttributesV1.iMore = EFalse;
}
//
// Debug logging of the PDU we are sending...
//
LOGSMSIFPDU(_L8("ETEL TX PDU: "), iSmsArray[iSmsPDUData.iSent].Pdu(), ETrue);
LOGSMSIFSENDATTRIBUTES(_L8("SENDATTRIBS: "), iMobileSmsSendAttributesV1);
LOGSMSIFTIMESTAMP();
//
// Send the message and enter the correct state...
//
iState = ESmsMessageSendPDU;
iSmsMessaging.SendMessage(iStatus, iSmsArray[iSmsPDUData.iSent].Pdu(),
iMobileSmsSendAttributesV1Pckg);
TimedSetActive(iSmsSettings.SendTryTimeout());
}
} // CSmsMessageSend::SendNextPDU
void CSmsMessageSend::UpdateSegmentationStore()
{
iState = ESmsMessageSendUpdatingSegmentationStore;
TRAPD(ret,DoUpdateSegmentationStoreL());
CompleteMyself(ret);
} // CSmsMessageSend::UpdateSegmentationStore
void CSmsMessageSend::DoUpdateSegmentationStoreL()
{
LOGSMSPROT1("CSmsMessageSend::DoUpdateSegmentationStoreL()");
iSmsPDUData.iSent++; // This is the end of the sequence for sending an SMS PDU
if (iSmsMessage->Scheme() == EControlParametersScheme ||
iSmsMessage->Scheme() == ETPSRRScheme)
{
iSegmentationStore.AddReferenceStatusPairL(*iSmsMessage,(TInt) iMobileSmsSendAttributesV1.iMsgRef, iSegmentSequenceNumber);
}
else if (iSmsMessage->Scheme() == EDefaultScheme)
{
iSegmentationStore.AddReferenceL(*iSmsMessage,(TInt) iMobileSmsSendAttributesV1.iMsgRef);
}
else
{
User::Leave(KErrNotFound);
}
} // CSmsMessageSend::DoUpdateSegmentationStoreL
void CSmsMessageSend::UpdateLogServerEvent()
{
LOGSMSPROT1("CSmsMessageSend::UpdateLogServerEvent()");
//
// Fill in log event...
//
iState = ESmsMessageSendUpdatingLogEvent;
iSmsPDUData.iTotal = iSmsArray.Count();
iSmsEventLogger->ChangeEvent(iStatus, *iSmsMessage, iSmsPDUData);
SetActive();
} // CSmsMessageSend::UpdateLogServerEvent
void CSmsMessageSend::Complete(TInt aStatus)
{
LOGSMSPROT3("CSmsMessageSend::Complete [iStatus=%d, iState=%d]", iStatus.Int(), iState );
//
// For completion during log events, use the saved error code instead...
//
if (iState == ESmsMessageSendCreatingLogServerEvent ||
iState == ESmsMessageSendUpdatingLogEvent)
{
aStatus = iSendError;
}
//
// Clear all PDU data that was sent...
//
for (TInt i=0; i < iSmsPDUData.iSent; i++)
{
iSmsArray.Delete(0);
}
iSmsArray.Reset();
iState = ESmsMessageSendIdle;
//
// Call the base function to perform the actual complete...
//
CSmsuActiveBase::Complete(aStatus);
} // CSmsMessageSend::Complete