Silent Receiving of SMS messages

来源:互联网 发布:风雅软件 编辑:程序博客网 时间:2024/05/16 03:33

If you ever wanted to receive an SMS without user notification, it might have occurred to you that using standard CSmsClientMtm APIs is not the best option. Sometimes it's possible to delete an incoming message before your phone beeps, but we can't rely on this. Luckily, we have a different approach which will help us to take care about user's rest. I'm talking about receiving SMS through sockets.

We will need the TSmsAddr class which is not a part of the public SDK since Series60 3rd MR. Now you can find it inside the SMSUtilities folder of the API plug-in package.

The basic idea is to bind a RSocket to the SMS channel and wait till something would appear there. We have several different ways of opening the SMS socket defined in the TSmsAddrFamily enum:

enum TSmsAddrFamily    {    ESmsAddrUnbound              = 0, // Not bound yet    ESmsAddrSendOnly             = 1, // Only for sending, no reception    ESmsAddrMessageIndication    = 2, // Matches on IEI 0x01 and DCS 0x110(1)0(1)xxxx    ESmsAddrMatchIEI             = 3, // For matching Information Element Identifiers    ESmsAddrMatchText            = 4, // For matching any text patterns    ESmsAddrRecvAny              = 5, // Receive all messages. Only one client allowed    ESmsAddrStatusReport         = 6, // For receiving Status Reports    ESmsAddrLocalOperation       = 7, // For local SIM operations    ESmsAddrApplication8BitPort  = 8, // For sock port identification    ESmsAddrApplication16BitPort = 9, // For sock port identification    ESmsAddrEmail                = 10 // For matching of email messages    };

ESmsAddrRecvAny looks tempting, but the built-in SMS application has already bound it. Our next choice is ESmsAddrMatchText. We can catch any message that starts with the required symbol sequence defined through SetTextMatch(). I'm using '#:' as an example, but you are free to setup any necessary pattern, even an empty one, that will allow you to intercept all messages.

Alternatively, you can select messages not by a prefix, but by a port number using ESmsAddrApplication8BitPort or ESmsAddrApplication16BitPort. It's useful for the inter-application communication. For example, client/server applications can exchange messages via SMS on the defined port.

One more note before digging into the code. Even after you read the message from the SMS socket, the actual data is still persisted in the receiving queue. You have to sent the KIoctlReadMessageSucceeded command using the Ioctl() function to indicate, that the message was successfully received. Otherwise, all the messages you intercepted will appear in the native SMS inbox on the next reboot.

And now less talk, more code.

SmsSocketEngine.h

#ifndef SMSSOCKETENGINE_H#define SMSSOCKETENGINE_H// INCLUDES #include <e32base.h>#include <es_sock.h>                    // RSocketServ#include <f32file.h>                    // RFs// LIBS// esock.lib (RSocketServ), smsu.lib (TSmsAddr), gsmu.lib (CSmsBuffer),// efsrv.lib (RFs), estor.lib (RSmsSocketReadStream)// CAPS// NetworkServices (RSocket::Bind), ReadUserData (RSocket::Bind), // WriteUserData (RSocket::Bind)// FORWARD DECLARATIONSclass MSmsEngineObserver;// CLASS DECLARATION/** * CSmsSocketEngine class. * CSmsSocketEngine declaration. *  */class CSmsSocketEngine : public CActive    {    public: // Constructors and destructor        /**         * NewL()         * Creates new CSmsSocketEngine object.         * @param aObserver Reference to the MSmsEngineObserver object.         * @return Pointer to the created instance of CSmsSocketEngine.         */        static CSmsSocketEngine* NewL(MSmsEngineObserver& aObserver);        /**         * NewLC()         * Creates new CSmsSocketEngine object.         * @param aObserver Reference to the MSmsEngineObserver object.         * @return Pointer to the created instance of CSmsSocketEngine.         */        static CSmsSocketEngine* NewLC(MSmsEngineObserver& aObserver);        /**         * ~CSmsSocketEngine()         * Destructor.         */        ~CSmsSocketEngine();    protected: // From CActive        /**         * DoCancel()         * Implements cancellation of an outstanding request.         */        void DoCancel();        /**         * RunL()         * Handles an active objects request completion event.         */        void RunL();        /**         * RunError()         * Handles a leave occurring in the request completion event handler RunL().         * @param aError The leave code.         * @return KErrNone if error was handled, otherwise system-wide error.         */        TInt RunError(TInt aError);            private: // New functions                /**         * Start()         * Starts waiting for the actual socket data.         */        void Start();    public: // New functions                /**         * StartListeningL()         * Starts listening for an incoming SMS.         */        void StartListeningL();                /**         * StopListening()         * Stops listening.         */        void StopListening();    private: // Constructors        /**         * CSmsSocketEngine()         * Default C++ constructor.         * @param aObserver Reference to the MSmsEngineObserver object.         */        CSmsSocketEngine(MSmsEngineObserver& aObserver);        /**         * ConstructL()         * Default EPOC constructor.         */        void ConstructL();    private: // enum                enum TSmsSocketEngineState            {            ESmsIdle,            ESmsListening,            ESmsSystemNotyfing            };    private: // data        MSmsEngineObserver&     iObserver;                RSocketServ             iSocketServ;        RSocket                 iReadSocket;                RFs                     iFs;                TBool                   iWait;        TPckgBuf<TUint>         iBuf;                TSmsSocketEngineState   iState;    };#endif // SMSSOCKETENGINE_H

SmsSocketEngine.cpp

// INCLUDE FILES #include "SmsSocketEngine.h"            // CSmsSocketEngine#include "SmsEngineObserver.h"          // MSmsEngineObserver#include <smsuaddr.h>                   // TSmsAddr#include <gsmubuf.h>                    // CSmsBuffer#include <smsustrm.h>                   // RSmsSocketReadStream#include <gsmumsg.h>                    // CSmsMessage    // ================= MEMBER FUNCTIONS ========================================//// ---------------------------------------------------------------------------// CSmsSocketEngine::CSmsSocketEngine(MSmsEngineObserver& aObserver)// Default C++ constructor.// ---------------------------------------------------------------------------//CSmsSocketEngine::CSmsSocketEngine(MSmsEngineObserver& aObserver) :    CActive(EPriorityStandard),    iObserver(aObserver)    {    }// ---------------------------------------------------------------------------// CSmsSocketEngine::~CSmsSocketEngine()// Destructor.// ---------------------------------------------------------------------------//CSmsSocketEngine::~CSmsSocketEngine()    {    // cancel any request, if outstanding    Cancel();        iReadSocket.Close();    iFs.Close();    iSocketServ.Close();    }// ---------------------------------------------------------------------------// CSmsSocketEngine::NewL(MSmsEngineObserver& aObserver)// Two-phased constructor.// ---------------------------------------------------------------------------//CSmsSocketEngine* CSmsSocketEngine::NewL(MSmsEngineObserver& aObserver)    {    CSmsSocketEngine* self = CSmsSocketEngine::NewLC(aObserver);    CleanupStack::Pop(self);    return self;    }// ---------------------------------------------------------------------------// CSmsSocketEngine::NewLC(MSmsEngineObserver& aObserver)// Two-phased constructor.// ---------------------------------------------------------------------------//CSmsSocketEngine* CSmsSocketEngine::NewLC(MSmsEngineObserver& aObserver)    {    CSmsSocketEngine* self = new (ELeave) CSmsSocketEngine(aObserver);    CleanupStack::PushL(self);    self->ConstructL();    return self;    }// ---------------------------------------------------------------------------// CSmsSocketEngine::ConstructL()// Default EPOC constructor.// ---------------------------------------------------------------------------//void CSmsSocketEngine::ConstructL()    {    CActiveScheduler::Add(this);        User::LeaveIfError(iSocketServ.Connect());    User::LeaveIfError(iFs.Connect());        StartListeningL();    }// ---------------------------------------------------------------------------// CSmsSocketEngine::DoCancel()// Implements cancellation of an outstanding request.// ---------------------------------------------------------------------------//void CSmsSocketEngine::DoCancel()    {    iReadSocket.CancelIoctl();    iState = ESmsIdle;    }// ---------------------------------------------------------------------------// CSmsSocketEngine::RunL()// Handles an active objects request completion event.// ---------------------------------------------------------------------------//void CSmsSocketEngine::RunL()    {    if (iStatus == KErrNone)        {        if (iState == ESmsListening)            {            // allocate SMS buffer            CSmsBuffer* buffer = CSmsBuffer::NewL();            CleanupStack::PushL(buffer);                        // create new incoming message, pass ownership of the buffer!            CSmsMessage* message = CSmsMessage::NewL(iFs,                                                      CSmsPDU::ESmsDeliver,                                                      buffer);            CleanupStack::Pop(buffer);            CleanupStack::PushL(message);            // open socket read stream            RSmsSocketReadStream readStream(iReadSocket);            CleanupClosePushL(readStream);            // read message            message->InternalizeL(readStream);            CleanupStack::PopAndDestroy(&readStream);                        TPtrC number = message->ToFromAddress();            // extract the message body            HBufC* body = HBufC::NewLC(message->Buffer().Length());            TPtr bodyPtr(body->Des());            message->Buffer().Extract(bodyPtr, 0, message->Buffer().Length());            iObserver.MessageReceived(number, *body);            CleanupStack::PopAndDestroy(2, message); // body, message                        // notify system about successful receiving            iReadSocket.Ioctl(KIoctlReadMessageSucceeded, iStatus,                               NULL, KSolSmsProv);            iState = ESmsSystemNotyfing;            SetActive();            }        else            {            Start();            }        }    else        {        iObserver.HandleError(iStatus.Int());        }    }// ---------------------------------------------------------------------------// CSmsSocketEngine::RunError(TInt aError)// Handles a leave occurring in the request completion event handler RunL().// ---------------------------------------------------------------------------//TInt CSmsSocketEngine::RunError(TInt aError)    {    iObserver.HandleError(aError);    return KErrNone;    }// ---------------------------------------------------------------------------// CSmsSocketEngine::Start()// Starts waiting for the actual socket data.// ---------------------------------------------------------------------------//void CSmsSocketEngine::Start()    {    // wait for an incoming data    iReadSocket.Ioctl(KIOctlSelect, iStatus, &iBuf, KSOLSocket);    iState = ESmsListening;    SetActive();    }// ---------------------------------------------------------------------------// CSmsSocketEngine::StartListeningL()// Starts listening for an incoming SMS.// ---------------------------------------------------------------------------//void CSmsSocketEngine::StartListeningL()    {    // we can't handle several requests simultaneously    if (IsActive())        {        User::Leave(KErrNotReady);        }    // just in case    iReadSocket.Close();        // open read socket    User::LeaveIfError(iReadSocket.Open(iSocketServ,                                        KSMSAddrFamily,                                        KSockDatagram,                                        KSMSDatagramProtocol));    _LIT8(KMathTag, "#:");    // set match pattern    TSmsAddr smsAddr;     smsAddr.SetSmsAddrFamily(ESmsAddrMatchText);     smsAddr.SetTextMatch(KMathTag); // put KNullDesC8 to catch all messages        // use this to read the message from a certain port    //smsAddr.SetSmsAddrFamily(ESmsAddrApplication8BitPort);    //smsAddr.SetPort(16500); // GSM Application port from 16000 to 16999         // bind the socket    User::LeaveIfError(iReadSocket.Bind(smsAddr));    iBuf() = KSockSelectRead;        Start();    }// ---------------------------------------------------------------------------// CSmsSocketEngine::StopListening()// Stops listening.// ---------------------------------------------------------------------------//void CSmsSocketEngine::StopListening()    {    Cancel();        iReadSocket.Close();    }

P.S.: There is such an example on Wiki Nokia, unfortunately it's very buggy.

原创粉丝点击