diff -r 000000000000 -r 3553901f7fa8 telephonyserverplugins/multimodetsy/Multimode/sms/mSMSSEND.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telephonyserverplugins/multimodetsy/Multimode/sms/mSMSSEND.CPP Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,641 @@ +// 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 of CATSmsMessagingSend. +// +// + +/** + @file +*/ + + +#include + +#include "NOTIFY.H" +#include "mSMSMESS.H" +#include "mSMSSEND.H" +#include "mSLOGGER.H" +#include "ATIO.H" +#include "smsutil.h" // for CATSmsUtils + +// +// Constants +// +const TInt KPduMode=0; + +// +// Macros +// + +#ifdef __LOGDEB__ +_LIT8(KLogEntry,"CATSmsMessagingSend::%S\t%S"); +#define LOCAL_LOGTEXT(function,text) {_LIT8(F,function);_LIT8(T,text);LOGTEXT3(KLogEntry,&F,&T);} +#else +#define LOCAL_LOGTEXT(function,text) +#endif + +/** + * CANCEL_AND_RETURN_IF_NEEDED + * Used to implement cancellation at safe points inside CATSmsMessagingSend::EventSignal + */ +#define CANCEL_AND_RETURN_IF_NEEDED()\ +{\ +if(iStop)\ + {\ + Complete(KErrCancel);\ + LOCAL_LOGTEXT("CANCEL_AND_RETURN_IF_NEEDED","Cancelled");\ + return;\ + }\ +} + + +// +// Class Implementation +// +CATSmsMessagingSend* CATSmsMessagingSend::NewL( CATIO* aIo, + CTelObject* aTelObject, + CATInit* aInit, + CPhoneGlobals* aGsmStatus) +/** + * Creates a new instance of CATSmsMessagingSend + */ + { + CATSmsMessagingSend* self=new(ELeave) CATSmsMessagingSend(aIo,aTelObject,aInit,aGsmStatus); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +CATSmsMessagingSend::CATSmsMessagingSend( CATIO* aIo, + CTelObject* aTelObject, + CATInit* aInit, + CPhoneGlobals* aGsmStatus) + :CATSmsCommands(aIo,aTelObject,aInit,aGsmStatus) +/** + * C++ constructor + */ +{} + +void CATSmsMessagingSend::ConstructL() +/** + * 2nd phase contructor + */ + { + CATCommands::ConstructL(); + iMsgDataAscii.Zero(); // Just in case + } + +CATSmsMessagingSend::~CATSmsMessagingSend() +/** + * C++ destructor + */ + { + delete iMsgData; + } + +void CATSmsMessagingSend::Start(TTsyReqHandle aTsyReqHandle,TAny* aParams) +/** + * Starts the procedure of sending an SMS + * @param aTsyReqHandle Handle to client, to be completed when operation done + * @param aParams Pointer to the SMS PDU (with or without SC prefixed) to be sent + */ + { + LOCAL_LOGTEXT("Start","Have been requested to send a message"); + + // Intialize data + iHaveRetriedWithOtherPduStd=EFalse; + iStop=EFalse; + + // ::SetMsgAttributes must be called before ::Start is called + __ASSERT_DEBUG(iMsgAttributes,Panic(EATSmsMessagingSendNullMsgAttributes)); + + // Save access to the data we be given thru our parameters + iReqHandle=aTsyReqHandle; + delete iMsgData; // Ensure we don't orphan some memory + iMsgData=((TDesC8*)aParams)->Alloc(); + if(!iMsgData) + Complete(KErrNoMemory); + + // Kick off first AT stuff + LOCAL_LOGTEXT("Start","Setting phone to PDU mode (as opposed to text mode)"); + iTxBuffer.Format(KSmsFormatCommand,KPduMode); + WriteTxBufferToPhone(ESetPhoneToPDUMode); + } + + +void CATSmsMessagingSend::StartFindSCA() +/** + * Attempt to find an SCA to use when sending message. + * The following SCA sources are checked in this order... + * Supplied by client in iMsgAttributes.iSc + * Use default SCA in the phone's memory + * Use default SCA in the phone's memory after doing 'AT+CRES=1' + */ + { + LOCAL_LOGTEXT("StartFindSCA",""); + + // Did client supply SCA in iMsgAttributes.iSc + if(iMsgAttributes->iFlags&RMobileSmsMessaging::KGsmServiceCentre && iMsgAttributes->iGsmServiceCentre.iTelNumber.Length()!=0) + { + iMsgSCA=iMsgAttributes->iGsmServiceCentre; + DoneFindSCA(); + } + + // Does the phone have a default SCA in memory + else + { + TInt ret=CATSmsCommands::RequestATCommand(CATSmsCommands::EGetSCAFromPhone); + if(ret!=KErrNone) + Complete(ret); + } + + } + + +void CATSmsMessagingSend::DoneFindSCA() + { + LOCAL_LOGTEXT("DoneFindSCA",""); + // At this point in the execution we can guarantee that... + // iMsgData holds the PDU to be sent in binary format without a prepended SCA + // iMsgSCA holds the SCA to be used to send the PDU + + // Check we can handle the Type-Of-Address of the SCA + if(!(iMsgSCA.iNumberPlan==RMobilePhone::EIsdnNumberPlan && + (iMsgSCA.iTypeOfNumber==RMobilePhone::EInternationalNumber || + iMsgSCA.iTypeOfNumber==RMobilePhone::EUnknownNumber))) + { + LOCAL_LOGTEXT("DoneFindSCA","SCA type unsupported"); + Complete(KErrCorrupt); + return; + } + + // Check which ETSI standard we think phone conforms to + if(PhoneUsesNewPDUStandard()) + SendMessageToNewPhone(); + else + SendMessageToOldPhone(); + } + + +void CATSmsMessagingSend::SendMessageToOldPhone() +/** + * Send message to phone which uses new ETSI standard (no SCA prefixed to PDU expected, + * use phone's default SCA setting instead). + */ + { + LOCAL_LOGTEXT("SendMessageToOldPhone",""); + // Set the SCA to use as the default in the phone's memory + iRequestSCA=iMsgSCA; + TInt ret=CATSmsCommands::RequestATCommand(CATSmsCommands::ESetSCAInPhone); + if(ret!=KErrNone) + Complete(ret); + } + + +void CATSmsMessagingSend::SendMessageToOldPhone_Stage2() + { + // Convert PDU from binary to ASCII + iMsgDataAscii.Zero(); + CATSmsUtils::AppendDataToAscii(iMsgDataAscii,*iMsgData); + if(iMsgDataAscii.Length()==0) + { + LOCAL_LOGTEXT("SendMessageToOldPhone_Stage2","Failed to convert binary PDU to ASCII"); + Complete(KErrCorrupt); + return; + } + + // Send PDU length to the phone + const TInt pduLengthSemiOctets(iMsgDataAscii.Length()); + const TInt pduLengthOctets(pduLengthSemiOctets/2+pduLengthSemiOctets%2); + iTxBuffer.Format(KSmsSendPduLengthCommand,pduLengthOctets); + WriteTxBufferToPhone(ESendPDULengthToPhone); + } + +void CATSmsMessagingSend::SendPDUToPhone() + { + LOCAL_LOGTEXT("SendPDUToPhone",""); + // Send PDU to phone + iTxBuffer.Format(iMsgDataAscii); + iTxBuffer.Append(KCtrlZChar); + WriteTxBufferToPhone(ESendPDUToPhone); + } + +void CATSmsMessagingSend::SendPDUToPhone_Stage2() + { + // Get the message reference number & submit report and then we've finished + Complete(ParseCMGSResponse()); + } + +void CATSmsMessagingSend::SendMessageToNewPhone() +/** + * Send message to phone which uses new ETSI standard (SCA prefixed to PDU expected). + */ + { + LOCAL_LOGTEXT("SendMessageToNewPhone",""); + + // Convert SCA to ASCII (ensure that 03.40 format is used to create SCA) + iMsgDataAscii.Zero(); + if(CATSmsUtils::AppendAddressToAscii(iMsgDataAscii,iMsgSCA,ETrue)!=KErrNone || iMsgDataAscii.Length()==0) + { + LOCAL_LOGTEXT("SendMessageToNewPhone","Failed to prepend SCA to PDU"); + Complete(KErrCorrupt); + return; + } + + // Convert PDU to ASCII + const TInt msgDataAsciiLen(iMsgDataAscii.Length()); + CATSmsUtils::AppendDataToAscii(iMsgDataAscii,*iMsgData); + if(iMsgDataAscii.Length()==msgDataAsciiLen) + { + LOCAL_LOGTEXT("SendMessageToNewPhone","Failed to convert binary PDU to ASCII"); + Complete(KErrCorrupt); + return; + } + + // Send PDU length to the phone + const TInt pduLengthSemiOctets(iMsgDataAscii.Length()-msgDataAsciiLen); // Must not include the prefixed SCA in calculation + const TInt pduLengthOctets(pduLengthSemiOctets/2+pduLengthSemiOctets%2); + iTxBuffer.Format(KSmsSendPduLengthCommand,pduLengthOctets); + WriteTxBufferToPhone(ESendPDULengthToPhone); + } + + +void CATSmsMessagingSend::EventSignal(TEventSource aSource) +/** + * Handle the events from the comm port + * + * This code is first called after the phone has been set to PDU mode. + * First we find the SCA that we are going to use. + * Secondly we send the message to the phone depending on whether the phone seems to be using + * the new ETSI format (SCA prefixed to PDU expected) or the old ETSI format (no SCA prefixed + * to PDU expected). + * + * @param aSource denotes if event is due to read, write or timeout + */ + { + LOCAL_LOGTEXT("EventSignal",""); + LOGTEXT3(_L8("iState=%D iSource=%D"),iState,aSource); + LOGTEXT2(_L8("iStop=%D"),iStop); + + // Check to see if this class or our base class need to process the event + if(CATSmsCommands::RequestATActive()) + { + // + // Allow base class to handle event, and find out if a request completed + // + CATSmsCommands::EventSignal(aSource); + const CATSmsCommands::TRequest reqCompleted(CATSmsCommands::RequestATCompleted()); + + if(reqCompleted!=ENone) + CANCEL_AND_RETURN_IF_NEEDED(); + + // Otheriwse, Check if a request completed as a result of the event processing + switch(reqCompleted) + { + case CATSmsCommands::EGetSCAFromPhone: + LOCAL_LOGTEXT("EventSignal","EGetSCAFromPhone completed"); + if(iRequestError==KErrNone) + { + iMsgSCA=iRequestSCA; + DoneFindSCA(); + } + else + { + LOCAL_LOGTEXT("EventSignal","Failed to find an SCA to use"); + Complete(iRequestError,aSource); + } + break; + + case CATSmsCommands::ESetSCAInPhone: + LOCAL_LOGTEXT("EventSignal","ESetSCAInPhone completed"); + if(iRequestError==KErrNone) + SendMessageToOldPhone_Stage2(); + else + { + LOCAL_LOGTEXT("EventSignal","Failed to set SCA in phone"); + Complete(iRequestError,aSource); + } + break; + + case CATSmsCommands::ENone: // Must not be caught by default case + break; + default: + LOCAL_LOGTEXT("EventSignal","CATSmsCommands unknown request completed"); + __ASSERT_DEBUG(EFalse,Panic(EATSmsMessagingUnknownRequestCompleted)); + } + } + else + { + // + // This class will handle event + // + if (aSource==ETimeOutCompletion) + { + LOCAL_LOGTEXT("EventSignal","Timeout error"); + iIo->WriteAndTimerCancel(this); + Complete(KErrTimedOut, aSource); + } + + TInt ret(KErrNone); + switch(iState) + { + case ESetPhoneToPDUMode: + if(aSource==EWriteCompletion) + HandleWriteCompletion(aSource); + else + { + ret=HandleResponseCompletion(aSource,EFalse); + if (ret!=KErrNone) + { + Complete(ret,aSource); + return; + } + StartFindSCA(); + } + break; + + case ESendPDULengthToPhone: + if(aSource==EWriteCompletion) + { + HandleWriteCompletion(aSource); + iExpectString=iIo->AddExpectString(this,KSmsEnterPduModeResponse,ETrue); + } + else + { + ret=HandleResponseCompletion(aSource,EFalse); + if (ret!=KErrNone) + { + Complete(ret,aSource); + return; + } + ret=ConvertCMSErrorToKErr(CMSErrorValue()); + iIo->RemoveExpectString(iExpectString); + iExpectString=NULL; + if (ret!=KErrNone) + { + Complete(ret,aSource); + return; + } + + // If we get a Cancel request just before we send the PDU, we instead send an escape + // character to trigger a PDU send failure and then continue as normal. + if (iStop) + { + LOCAL_LOGTEXT("EventSignal","Cancel requested. Sending Escape Character instead of PDU"); + + // In case of Ericsson phone, after sending the escape char we are not receiving expected + // response (OK/ERROR), it is just echoing those char. But if the escape char is prefixed + // by "at", then it responds with expected response. + + // But in case of other phones, we believe the escape char alone works. We have confirmed + // this with Nokia phones. + + _LIT16(KEricsson,"*ERICSSON*"); + if(iPhoneGlobals->iPhoneId.iManufacturer.MatchF(KEricsson)==0) + { + _LIT8(KEscapeSequenceString,"at%S\r"); + iTxBuffer.Format(KEscapeSequenceString,&KEscapeChar); + } + else + { + iTxBuffer.Format(KEscapeChar); + } + WriteTxBufferToPhone(ESendEscapeCharToPhone); + } + else + SendPDUToPhone(); + } + break; + + case ESendPDUToPhone: + if(aSource==EWriteCompletion) + { + HandleWriteCompletion(aSource); + } + else + { + const TInt err=ConvertCMSErrorToKErr(CMSErrorValue()); + ret=HandleResponseCompletion(aSource,EFalse); + if (ret!=KErrNone) + { + Complete(ret,aSource); + return; + } + + // Check if an error occurred + if(err!=KErrNone) + { + // If we have not already, then retry using the other Pdu standard + if(!iHaveRetriedWithOtherPduStd) + { + CANCEL_AND_RETURN_IF_NEEDED(); + // Retry with new standard + LOCAL_LOGTEXT("EventSignal","Failed to send message, will retry using other phone standard"); + iHaveRetriedWithOtherPduStd=ETrue; + TogglePhonePDUStandard(); + DoneFindSCA(); + } + else + { + // We have tried both PDU standards and have failed :-( + LOCAL_LOGTEXT("EventSignal","Failed to send message :-("); + Complete(err,aSource); + } + } + else + { + // Success, we have sent the message ;-) + if (iStop) {LOCAL_LOGTEXT("EventSignal","Message send successful, Cancel request to late, PDU already sent");} + else {LOCAL_LOGTEXT("EventSignal","Message send successful ;-)");} + SendPDUToPhone_Stage2(); + } + } + break; + + case ESendEscapeCharToPhone: + if(aSource==EWriteCompletion) + { + HandleWriteCompletion(aSource); + } + else + { + ret=HandleResponseCompletion(aSource,EFalse); + if (ret!=KErrNone) + { + Complete(ret,aSource); + return; + } + Complete(KErrCancel,aSource); + } + break; + + case ENotInProgress: // Required to stop 'unhandled enum' warning with ARM4 + break; + } + } + } + + +void CATSmsMessagingSend::Complete(TInt aError,TEventSource aSource) + { + LOCAL_LOGTEXT("Complete",""); + LOGTEXT3(_L8("aError=%D aSource=%D"),aError,aSource); + + iIo->WriteAndTimerCancel(this); + iIo->RemoveExpectStrings(this); + iOKExpectString = NULL; + iErrorExpectString = NULL; + CATCommands::Complete(aError,aSource); + iTelObject->ReqCompleted(iReqHandle, aError); + if (aSource==EWriteCompletion) + iIo->Read(); + iState = ENotInProgress; + } + +void CATSmsMessagingSend::SetMsgAttributes(RMobileSmsMessaging::TMobileSmsSendAttributesV1* aMsgAttributes) + { + iMsgAttributes=aMsgAttributes; + } + +void CATSmsMessagingSend::Stop(TTsyReqHandle aTsyReqHandle) +// +// Attempts to halt the process +// + { + LOCAL_LOGTEXT("Stop","Client has requested cancel"); + __ASSERT_ALWAYS(aTsyReqHandle == iReqHandle, Panic(EIllegalTsyReqHandle)); + + // Ensure our base class notes that a cancel has been requested + CATSmsCommands::RequestATCommandCancel(); + + // Set our flag to denote we should cancel as soon as possible + iStop=ETrue; + } + +void CATSmsMessagingSend::CompleteWithIOError(TEventSource /*aSource*/,TInt aStatus) + { + if (iState!=ENotInProgress) + { + iIo->WriteAndTimerCancel(this); + iTelObject->ReqCompleted(iReqHandle, aStatus); + iState = ENotInProgress; + } + } + +// +// Utility functions +// +TBool CATSmsMessagingSend::PhoneUsesNewPDUStandard() +/** + * This code assumes that EPhoneTestOldStandard and EPhoneTestUndefined are + * the same. + */ + { + return (iPhoneGlobals->iPhoneTestState==CPhoneGlobals::EPhoneTestNewStandard); + } + +void CATSmsMessagingSend::TogglePhonePDUStandard() +/** + * This code assumes that EPhoneTestOldStandard and EPhoneTestUndefined are + * the same. + */ + { + if(iPhoneGlobals->iPhoneTestState==CPhoneGlobals::EPhoneTestNewStandard) + iPhoneGlobals->iPhoneTestState=CPhoneGlobals::EPhoneTestOldStandard; + else + iPhoneGlobals->iPhoneTestState=CPhoneGlobals::EPhoneTestNewStandard; + } + +void CATSmsMessagingSend::WriteTxBufferToPhone(TState aNewState) +/** + * Sends the contents of iTxBuffer to the phone + */ + { + iIo->Write(this,iTxBuffer); + iIo->SetTimeOut(this,KATWriteTimeout); + iState=aNewState; + } + + +TInt CATSmsMessagingSend::ParseCMGSResponse() +/** + * Parse CMGS response string for Message Reference Number & + * SUBMIT-REPORT PDU (optional) and store their values in the clients data space. + * + * @return Standard KErr... values + */ + { + __ASSERT_DEBUG(iMsgAttributes,Panic(EATSmsMessagingSendNullMsgAttributes)); + + iBuffer.Set(iIo->Buffer()); + TInt pos=iBuffer.FindF(KCMGSResponseString); + if (pos==KErrNotFound) + { + LOCAL_LOGTEXT("ParseCMGSResponse","Cannot find '+CMGS:' string"); + return KErrNotFound; + } + + // Locate the message reference number + // (ie. read in all digits form the first found to the end of the string) + const TInt bufLength=iBuffer.Length(); + pos+=KCMGSResponseStringLength; + while(pos=bufLength) + { + LOCAL_LOGTEXT("ParseCMGSResponse","Cannot find any digits after '+CMGS:'"); + return KErrNotFound; + } + + // Read message number and store in clients data structure + TPtrC8 ptr=iBuffer.Mid(pos); + TLex8 lex(ptr); + TUint16 val; + TInt ret=lex.Val(val,EDecimal); + if(ret!=KErrNone) + { + LOCAL_LOGTEXT("ParseCMGSResponse","Unable to read Message Reference Number"); + return ret; + } + LOGTEXT2(_L8("CATSmsMessagingSend Message reference number %d"),val); + iMsgAttributes->iMsgRef=val; + iMsgAttributes->iFlags|=RMobileSmsMessaging::KMessageReference; + + // Locate SUBMIT-REPORT (it does not have to exist) + pos=iBuffer.FindF(KCommaChar); + if(pos!=KErrNotFound) + { + while(posiSubmitReport.Zero(); + while(posiSubmitReport.Append(TChar(iBuffer[pos])); + ++pos; + } + iMsgAttributes->iFlags|=RMobileSmsMessaging::KGsmSubmitReport; + } + } + + return KErrNone; + } + +