SMS Socket API
In the summary screen application, the CWeatherReportWatcher class waits for weather report messages. It does this by opening a socket to the SMS stack2 and registering for all SMS messages starting with 'Weather:'.
The 'Weather report' SMS encoding is very simple - it consists of the prefix 'Weather:' followed by a single digit, either '1', '2', or '3'. These correspond to 'sunny', 'cloudy' or 'rainy' - we don't get much interesting weather in the UK!
Example 8.11 shows the declaration of CWeatherReportWatcher that is later used to intercept the weather report messages.
class CWeatherReportWatcher : public CActive {
private:
RSocketServ iSocketServer; RSocket iSmsSocket;
enum TWeatherWatcherState {
EWaitingForWeatherReport, EAcknowledgingWeatherReport };
TWeatherWatcherState iState; };
Example 8.11 Class declaration for CWeatherReportWatcher
Example 8.11 shows how the CWeatherReportWatcher has two states.
1. Waiting for a weather report SMS message.
2. Acknowledging the received SMS message.
The weather report watcher is implemented as an active object because both the waiting and the acknowledging operations are potentially long running and therefore should be performed asynchronously. The active object allows these operations to run without blocking the entire thread. Note that if the thread was blocked then the message summaries would not be updated with any newly received messages.
Example 8.12 shows how the SMS socket is opened and how it registers an interest in SMS messages with the prefix ''Weather:'' by using RSocket::Bind().
_LIT8(KWeatherReportPrefixString, "Weather:");
2 For generic information on socket usage, see Chapter 3.
void CWeatherReportWatcher::ConstructL()
CActiveScheduler::Add(this);
// Connect to sockets server
// SMS messages are intercepted via sockets
User::LeaveIfError(iSocketServer.Connect());
// Open SMS socket
User::LeaveIfError(iSmsSocket.Open(iSocketServer, KSMSAddrFamily, KSockDatagram, KSMSDatagramProtocol));
// Set SMS prefix - only intercept SMS messages starting with a // particular string TSmsAddr smsAddress;
smsAddress.SetSmsAddrFamily(ESmsAddrMatchText);
smsAddress.SetTextMatch(KWeatherReportPrefixString);
iSmsSocket.Bind(smsAddress);
WaitForWeatherReportL(); }
Example 8.12 Registering to receive SMS messages with a specific prefix
WaitForWeatherReport() implements the code that actually waits for a weather report message to be received. This is shown in Example 8.13. Note how RSocket::Ioctl() is used to asynchronously wait for an SMS that matches the attributes that have already been set on the socket.
void CWeatherReportWatcher::WaitForWeatherReportL() {
// Wait for an appropriate SMS message
// the RunL will be called when an appropriate SMS message is received
// Note that the smsAddress has already been set in ConstructL so we // will only intercept messages starting with 'Weather:'
iSbuf()=KSockSelectRead;
iSmsSocket.Ioctl(KIOctlSelect, iStatus, &iSbuf, KSOLSocket); iState = EWaitingForWeatherReport;
Example 8.13 Waiting for a message with the registered prefix to arrive
When a weather report message has been received RunL() is called. RunL() is split into two parts, the first to handle a newly received weather report and the second to handle the completion of the acknowledgement.
void CWeatherReportWatcher::RunL()
if (iState == EWaitingForWeatherReport)
CSmsBuffer* smsBuffer=CSmsBuffer::NewL(); CleanupStack::PushL(smsBuffer);
CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, ESmsDeliver, smsBuffer);
// smsMessage has taken ownership of smsBuffer so remove it from the
// cleanup stack.
CleanupStack::Pop(smsBuffer);
CleanupStack::PushL(smsMessage);
RSmsSocketReadStream readstream(iSmsSocket);
// This function may leave readstream >> *smsMessage;
// Extract the text from the SMS buffer TBuf<KMaxWeatherReportLength> weatherReportBuf; TInt bufferLength = smsBuffer->Length();
if (bufferLength > KMaxWeatherReportLength)
bufferLength = KMaxWeatherReportLength; }
smsBuffer->Extract(weatherReportBuf, 0, bufferLength);
Example 8.14 Handling an SMS upon arrival
Example 8.14 shows how the contents of the SMS message are extracted from the SMS socket and how CSmsBuffer and CSmsMes-sage are used. Once the weather report has been extracted into a descriptor then it is processed by the code shown in Example 8.15:
MWeatherReportObserver::TWeatherReport weatherReport = MWeatherReportObserver::ENone;
// Process the message if (weatherReportBuf.Length() >= KMaxWeatherReportLength)
// Get the last character. The last character represents the // weather report.
TUint16 lastCharacter = weatherReportBuf[KMaxWeatherReportLength -1];
// Process the message switch (lastCharacter)
weatherReport = MWeatherReportObserver::ESunny; break; case '2':
weatherReport = MWeatherReportObserver::ECloudy; break;
weatherReport = MWeatherReportObserver::ERainy; break;
// Update the UI with the new weather report iWeatherReportObserver.NewWeatherReport(weatherReport);
CleanupStack::PopAndDestroy(smsMessage);
Example 8.15 Processing the received SMS
Messages with the contents "Weather:1" cause the UI to be updated with an ESunny weather report. Messages with contents ''Weather:2'' cause the UI to be updated with an ECloudy report, etc.
Once the incoming message has been processed then the application must acknowledge it. This lets the SMS stack know that the application has successfully dealt with the SMS so it can be deleted from the reassembly store.
If the current state of the system prevents the handling of this message, for example, due to low memory then the acknowledgement can be omitted so that the SMS stack will attempt to deliver the message to the application again and it can be processed later when the appropriate resources may be available.
Example 8.16 shows how the weather report is acknowledged.
// Acknowledge successful processing of the SMS message TPckgBuf<TUint> sbuf;
iSmsSocket.Ioctl(KIoctlReadMessageSucceeded, iStatus, &sbuf,
KSolSmsProv);
// Wait for the acknowledgement to be sent, go active. iState = EAcknowledgingWeatherReport;
Example 8.16 Acknowledging receipt of a received SMS
Finally, CWeatherReportWatcher waits for the acknowledgement to complete. Once this is done RunL() is called again, this time the acknowledgement branch is executed where it then waits for a new weather report message.
void CWeatherReportWatcher::RunL()
else if (iState == EAcknowledgingWeatherReport)
// The acknowledgement has now been sent so wait for another weather // report.
WaitForWeatherReportL(); }
Example 8.17 Waiting for the acknowledgement process to complete
Post a comment