Bug 736697 - Part 4: handle SMS-STATUS-REPORT. r=philikon
authorVicamo Yang <vyang@mozilla.com>
Thu, 05 Apr 2012 14:16:56 -0700
changeset 94426 2e2a8ba65b5ff9b3931457398742a3537e1a7e06
parent 94425 572810096bb67d03bfdf59ce31d657e6674a74e1
child 94427 8646a8cfc01aa0a66e1deb0eb8f876259d58e801
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilikon
bugs736697
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 736697 - Part 4: handle SMS-STATUS-REPORT. r=philikon
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -53,16 +53,17 @@ const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 const DATACALLINFO_CID =
   Components.ID("{ef474cd9-94f7-4c05-a31b-29b9de8a10d2}");
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer;
 
 const kSmsReceivedObserverTopic          = "sms-received";
+const kSmsDeliveredObserverTopic         = "sms-delivered";
 const DOM_SMS_DELIVERY_RECEIVED          = "received";
 const DOM_SMS_DELIVERY_SENT              = "sent";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/smsservice;1",
                                    "nsISmsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
@@ -146,16 +147,17 @@ function RadioInterfaceLayer() {
     connected:      null,
     roaming:        null,
     signalStrength: null,
     bars:           null,
     operator:       null,
     type:           null,
     msisdn:         null,
   };
+  this._sentSmsEnvelopes = {};
 }
 RadioInterfaceLayer.prototype = {
 
   classID:   RADIOINTERFACELAYER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
                                     classDescription: "RadioInterfaceLayer",
                                     interfaces: [Ci.nsIWorkerHolder,
                                                  Ci.nsIRadioInterfaceLayer]}),
@@ -252,16 +254,19 @@ RadioInterfaceLayer.prototype = {
         this.notifyRadioStateChanged();
         break;
       case "sms-received":
         this.handleSmsReceived(message);
         return;
       case "sms-sent":
         this.handleSmsSent(message);
         return;
+      case "sms-delivered":
+        this.handleSmsDelivered(message);
+        return;
       case "datacallstatechange":
         this.handleDataCallState(message.datacall);
         break;
       case "datacalllist":
         this.handleDataCallList(message);
         break;
       case "nitzTime":
         // TODO bug 714349
@@ -420,30 +425,71 @@ RadioInterfaceLayer.prototype = {
                                            DOM_SMS_DELIVERY_RECEIVED,
                                            message.sender || null,
                                            message.receiver || null,
                                            message.fullBody || null,
                                            message.timestamp);
     Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
   },
 
+  /**
+   * Local storage for sent SMS messages.
+   */
+  _sentSmsEnvelopes: null,
+  createSmsEnvelope: function createSmsEnvelope(options) {
+    let i;
+    for (i = 1; this._sentSmsEnvelopes[i]; i++) {
+      // Do nothing.
+    }
+
+    debug("createSmsEnvelope: assigned " + i);
+    options.envelopeId = i;
+    this._sentSmsEnvelopes[i] = options;
+  },
+
   handleSmsSent: function handleSmsSent(message) {
     debug("handleSmsSent: " + JSON.stringify(message));
+
+    let options = this._sentSmsEnvelopes[message.envelopeId];
+    if (!options) {
+      return;
+    }
+
     let timestamp = Date.now();
-    let id = gSmsDatabaseService.saveSentMessage(message.number,
-                                                 message.fullBody,
+    let id = gSmsDatabaseService.saveSentMessage(options.number,
+                                                 options.fullBody,
                                                  timestamp);
     let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_SENT,
                                            null,
-                                           message.number,
-                                           message.fullBody,
+                                           options.number,
+                                           options.fullBody,
                                            timestamp);
+
+    if (!options.requestStatusReport) {
+      // No more used if STATUS-REPORT not requested.
+      delete this._sentSmsEnvelopes[message.envelopeId];
+    } else {
+      options.sms = sms;
+    }
+
     //TODO handle errors (bug 727319)
-    gSmsRequestManager.notifySmsSent(message.requestId, sms);
+    gSmsRequestManager.notifySmsSent(options.requestId, sms);
+  },
+
+  handleSmsDelivered: function handleSmsDelivered(message) {
+    debug("handleSmsDelivered: " + JSON.stringify(message));
+
+    let options = this._sentSmsEnvelopes[message.envelopeId];
+    if (!options) {
+      return;
+    }
+    delete this._sentSmsEnvelopes[message.envelopeId];
+
+    Services.obs.notifyObservers(options.sms, kSmsDeliveredObserverTopic, null);
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     this._deliverDataCallCallback("dataCallStateChanged",
                                   [datacall.cid, datacall.ifname, datacall.state]);
@@ -916,23 +962,27 @@ RadioInterfaceLayer.prototype = {
   },
 
   sendSMS: function sendSMS(number, message, requestId, processId) {
     let options = this._calculateUserDataLength(message);
     options.type = "sendSMS";
     options.number = number;
     options.requestId = requestId;
     options.processId = processId;
+    options.requestStatusReport = true;
 
     this._fragmentText(message, options);
     if (options.segmentMaxSeq > 1) {
       options.segmentRef16Bit = this.segmentRef16Bit;
       options.segmentRef = this.nextSegmentRef;
     }
 
+    // Keep current SMS message info for sent/delivered notifications
+    this.createSmsEnvelope(options);
+
     this.worker.postMessage(options);
   },
 
   _callbacks: null,
   _enumerationCallbacks: null,
 
   registerCallback: function registerCallback(callback) {
     if (this._callbacks) {
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -610,16 +610,21 @@ let RIL = {
   /**
    * Hash map for received multipart sms fragments. Messages are hashed with
    * its sender address and concatenation reference number. Three additional
    * attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
    */
   _receivedSmsSegmentsMap: {},
 
   /**
+   * Outgoing messages waiting for SMS-STATUS-REPORT.
+   */
+  _pendingSentSmsMap: {},
+
+  /**
    * Mute or unmute the radio.
    */
   _muted: true,
   get muted() {
     return this._muted;
   },
   set muted(val) {
     val = Boolean(val);
@@ -1540,16 +1545,35 @@ let RIL = {
    * @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
    */
   _processSmsStatusReport: function _processSmsStatusReport(length) {
     let message = this._processReceivedSms(length);
     if (!message) {
       return PDU_FCS_UNSPECIFIED;
     }
 
+    let options = this._pendingSentSmsMap[message.messageRef];
+    if (!options) {
+      return PDU_FCS_OK;
+    }
+
+    delete this._pendingSentSmsMap[message.messageRef];
+
+    if ((options.segmentMaxSeq > 1)
+        && (options.segmentSeq < options.segmentMaxSeq)) {
+      // Not last segment. Send next segment here.
+      this._processSentSmsSegment(options);
+    } else {
+      // Last segment delivered with success. Report it.
+      this.sendDOMMessage({
+        type: "sms-delivered",
+        envelopeId: options.envelopeId,
+      });
+    }
+
     return PDU_FCS_OK;
   },
 
   /**
    * Helper for processing received multipart SMS.
    *
    * @return null for handled segments, and an object containing full message
    *         body once all segments are received.
@@ -1597,16 +1621,29 @@ let RIL = {
     if (DEBUG) {
       debug("Got full multipart SMS: " + JSON.stringify(options));
     }
 
     return options;
   },
 
   /**
+   * Helper for processing sent multipart SMS.
+   */
+  _processSentSmsSegment: function _processSentSmsSegment(options) {
+    // Setup attributes for sending next segment
+    let next = options.segmentSeq;
+    options.body = options.segments[next].body;
+    options.encodedBodyLength = options.segments[next].encodedBodyLength;
+    options.segmentSeq = next + 1;
+
+    this.sendSMS(options);
+  },
+
+  /**
    * Handle incoming messages from the main UI thread.
    *
    * @param message
    *        Object containing the message. Messages are supposed
    */
   handleDOMMessage: function handleMessage(message) {
     if (DEBUG) debug("Received DOM message " + JSON.stringify(message));
     let method = this[message.type];
@@ -1861,30 +1898,35 @@ RIL[REQUEST_OPERATOR] = function REQUEST
 RIL[REQUEST_RADIO_POWER] = null;
 RIL[REQUEST_DTMF] = null;
 RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) {
   options.messageRef = Buf.readUint32();
   options.ackPDU = Buf.readString();
   options.errorCode = Buf.readUint32();
 
   //TODO handle errors (bug 727319)
+
+  if (options.requestStatusReport) {
+    this._pendingSentSmsMap[options.messageRef] = options;
+  }
+
   if ((options.segmentMaxSeq > 1)
       && (options.segmentSeq < options.segmentMaxSeq)) {
-    // Setup attributes for sending next segment
-    let next = options.segmentSeq;
-    options.body = options.segments[next].body;
-    options.encodedBodyLength = options.segments[next].encodedBodyLength;
-    options.segmentSeq = next + 1;
-
-    this.sendSMS(options);
-    return;
+    // Not last segment
+    if (!options.requestStatusReport) {
+      // Status-Report not requested, send next segment here.
+      this._processSentSmsSegment(options);
+    }
+  } else {
+    // Last segment sent with success. Report it.
+    this.sendDOMMessage({
+      type: "sms-sent",
+      envelopeId: options.envelopeId,
+    });
   }
-
-  options.type = "sms-sent";
-  this.sendDOMMessage(options);
 };
 RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null;
 
 RIL.readSetupDataCall_v5 = function readSetupDataCall_v5(options) {
   if (!options) {
     options = {};
   }
   let [cid, ifname, ipaddr, dns, gw] = Buf.readStringList();
@@ -3053,16 +3095,18 @@ let GsmPDUHelper = {
    *        size will be userDataHeaderLength + 1; 0 for no header.
    * @param encodedBodyLength
    *        Length of the user data when encoded with the given DCS. For UCS2,
    *        in bytes; for 7-bit, in septets.
    * @param langIndex
    *        Table index used for normal 7-bit encoded character lookup.
    * @param langShiftIndex
    *        Table index used for escaped 7-bit encoded character lookup.
+   * @param requestStatusReport
+   *        Request status report.
    */
   writeMessage: function writeMessage(options) {
     if (DEBUG) {
       debug("writeMessage: " + JSON.stringify(options));
     }
     let address = options.number;
     let body = options.body;
     let dcs = options.dcs;
@@ -3139,16 +3183,21 @@ let GsmPDUHelper = {
     //            same OA
     // MTI:   bit1  bit0    Message Type
     //        0     0       SMS-DELIVER (SMSC ==> MS)
     //        0     1       SMS-SUBMIT (MS ==> SMSC)
 
     // PDU type. MTI is set to SMS-SUBMIT
     let firstOctet = PDU_MTI_SMS_SUBMIT;
 
+    // Status-Report-Request
+    if (options.requestStatusReport) {
+      firstOctet |= PDU_SRI_SRR;
+    }
+
     // Validity period
     if (validity) {
       //TODO: not supported yet, OR with one of PDU_VPF_*
     }
     // User data header indicator
     if (headerOctets) {
       firstOctet |= PDU_UDHI;
     }