Silent Receiving of SMS messages

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 
#include                     // RSocketServ
#include                     // 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 DECLARATIONS
class 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         iBuf;
        
        TSmsSocketEngineState   iState;
    };

#endif // SMSSOCKETENGINE_H

SmsSocketEngine.cpp

// INCLUDE FILES 
#include "SmsSocketEngine.h"            // CSmsSocketEngine
#include "SmsEngineObserver.h"          // MSmsEngineObserver

#include                    // TSmsAddr
#include                     // CSmsBuffer
#include                    // RSmsSocketReadStream
#include                     // 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.

你可能感兴趣的:(Silent Receiving of SMS messages)