Bug 873351 - Part 3: Refactor SMS Notifications from RadioInterfaceLayer to SmsService. r=echen
authorBevis Tseng <btseng@mozilla.com>
Thu, 27 Nov 2014 18:59:06 +0800
changeset 220451 c8057e079f2d6133f444f90ac7710316084abeef
parent 220450 99f8fe8f76a4fa82cd7b78d5de5115a4fe5370a8
child 220452 45e6036c278acb8230a887f8ada96246729d22ca
push id27989
push userryanvm@gmail.com
push dateFri, 19 Dec 2014 20:01:59 +0000
treeherdermozilla-central@0332f9af51b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechen
bugs873351
milestone37.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 873351 - Part 3: Refactor SMS Notifications from RadioInterfaceLayer to SmsService. r=echen
dom/mobilemessage/android/SmsService.cpp
dom/mobilemessage/gonk/MobileMessageDB.jsm
dom/mobilemessage/gonk/SmsService.js
dom/mobilemessage/interfaces/nsIGonkSmsService.idl
dom/mobilemessage/interfaces/nsISmsService.idl
dom/mobilemessage/ipc/SmsIPCService.cpp
dom/system/gonk/RadioInterfaceLayer.js
--- a/dom/mobilemessage/android/SmsService.cpp
+++ b/dom/mobilemessage/android/SmsService.cpp
@@ -46,24 +46,16 @@ SmsService::Send(uint32_t aServiceId,
     return NS_OK;
   }
 
   AndroidBridge::Bridge()->SendMessage(aNumber, aMessage, aRequest);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SmsService::IsSilentNumber(const nsAString& aNumber,
-                           bool*            aIsSilent)
-{
-  NS_NOTYETIMPLEMENTED("Implement me!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 SmsService::AddSilentNumber(const nsAString& aNumber)
 {
   NS_NOTYETIMPLEMENTED("Implement me!");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 SmsService::RemoveSilentNumber(const nsAString& aNumber)
--- a/dom/mobilemessage/gonk/MobileMessageDB.jsm
+++ b/dom/mobilemessage/gonk/MobileMessageDB.jsm
@@ -2963,21 +2963,21 @@ MobileMessageDB.prototype = {
 
         // The port information is only available in 1st segment for CDMA WAP Push.
         // If the segments of a WAP Push are not received in sequence
         // (e.g., SMS with seq == 1 is not the 1st segment received by the device),
         // we have to retrieve the port information from 1st segment and
         // save it into the segmentRecord.
         if (aSmsSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
             && seq === 1) {
-          if (aSmsSegment.originatorPort) {
+          if (aSmsSegment.originatorPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
             segmentRecord.originatorPort = aSmsSegment.originatorPort;
           }
 
-          if (aSmsSegment.destinationPort) {
+          if (aSmsSegment.destinationPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
             segmentRecord.destinationPort = aSmsSegment.destinationPort;
           }
         }
 
         if (segmentRecord.receivedSegments < segmentRecord.segmentMaxSeq) {
           if (DEBUG) debug("Message is incomplete.");
           segmentStore.put(segmentRecord);
           return;
--- a/dom/mobilemessage/gonk/SmsService.js
+++ b/dom/mobilemessage/gonk/SmsService.js
@@ -30,16 +30,18 @@ const kSmsFailedObserverTopic           
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 
 const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
 const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
 
+const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
+
 XPCOMUtils.defineLazyGetter(this, "gRadioInterfaces", function() {
   let ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
 
   let interfaces = [];
   for (let i = 0; i < ril.numRadioInterfaces; i++) {
     interfaces.push(ril.getRadioInterface(i));
   }
   return interfaces;
@@ -52,41 +54,60 @@ XPCOMUtils.defineLazyGetter(this, "gSmsS
 });
 
 XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
   let ns = {};
   Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
   return ns.PhoneNumberUtils;
 });
 
+XPCOMUtils.defineLazyGetter(this, "gWAP", function() {
+  let ns = {};
+  Cu.import("resource://gre/modules/WapPushManager.js", ns);
+  return ns;
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
+                                   "@mozilla.org/cellbroadcast/gonkservice;1",
+                                   "nsIGonkCellBroadcastService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIMobileConnectionService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
                                    "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
                                    "nsIRilMobileMessageDatabaseService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
                                    "nsIMobileMessageService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
+                                   "@mozilla.org/power/powermanagerservice;1",
+                                   "nsIPowerManagerService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsMessenger",
                                    "@mozilla.org/ril/system-messenger-helper;1",
                                    "nsISmsMessenger");
 
 let DEBUG = RIL.DEBUG_RIL;
 function debug(s) {
   dump("SmsService: " + s);
 }
 
 function SmsService() {
   this._silentNumbers = [];
   this.smsDefaultServiceId = this._getDefaultServiceId();
 
+  this._portAddressedSmsApps = {};
+  this._portAddressedSmsApps[gWAP.WDP_PORT_PUSH] = this._handleSmsWdpPortPush.bind(this);
+
+  this._receivedSmsSegmentsMap = {};
+
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
   Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 }
 SmsService.prototype = {
   classID: GONK_SMSSERVICE_CID,
 
   classInfo: XPCOMUtils.generateCI({classID: GONK_SMSSERVICE_CID,
@@ -146,16 +167,53 @@ SmsService.prototype = {
 
     if (!iccInfo) {
       return null;
     }
 
     return iccInfo.iccid;
   },
 
+  // The following attributes/functions are used for acquiring/releasing the
+  // CPU wake lock when the RIL handles the received SMS. Note that we need
+  // a timer to bound the lock's life cycle to avoid exhausting the battery.
+  _smsHandledWakeLock: null,
+  _smsHandledWakeLockTimer: null,
+  _acquireSmsHandledWakeLock: function() {
+    if (!this._smsHandledWakeLock) {
+      if (DEBUG) debug("Acquiring a CPU wake lock for handling SMS.");
+      this._smsHandledWakeLock = gPowerManagerService.newWakeLock("cpu");
+    }
+    if (!this._smsHandledWakeLockTimer) {
+      if (DEBUG) debug("Creating a timer for releasing the CPU wake lock.");
+      this._smsHandledWakeLockTimer =
+        Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    }
+    if (DEBUG) debug("Setting the timer for releasing the CPU wake lock.");
+    this._smsHandledWakeLockTimer
+        .initWithCallback(this._releaseSmsHandledWakeLock.bind(this),
+                          SMS_HANDLED_WAKELOCK_TIMEOUT,
+                          Ci.nsITimer.TYPE_ONE_SHOT);
+  },
+
+  _releaseSmsHandledWakeLock: function() {
+    if (DEBUG) debug("Releasing the CPU wake lock for handling SMS.");
+    if (this._smsHandledWakeLockTimer) {
+      this._smsHandledWakeLockTimer.cancel();
+    }
+    if (this._smsHandledWakeLock) {
+      this._smsHandledWakeLock.unlock();
+      this._smsHandledWakeLock = null;
+    }
+  },
+
+  _convertSmsMessageClassToString: function(aMessageClass) {
+    return RIL.GECKO_SMS_MESSAGE_CLASSES[aMessageClass] || null;
+  },
+
   _convertSmsMessageClass: function(aMessageClass) {
     let index = RIL.GECKO_SMS_MESSAGE_CLASSES.indexOf(aMessageClass);
 
     if (index < 0) {
       throw new Error("Invalid MessageClass: " + aMessageClass);
     }
 
     return index;
@@ -358,16 +416,357 @@ SmsService.prototype = {
     } catch (e) {
       if (DEBUG) {
         debug("Failed to _broadcastSmsSystemMessage: " + e);
       }
     }
   },
 
   /**
+   * Helper for processing received multipart SMS.
+   *
+   * @return null for handled segments, and an object containing full message
+   *         body/data once all segments are received.
+   *
+   * |_receivedSmsSegmentsMap|:
+   *   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: null,
+  _processReceivedSmsSegment: function(aSegment) {
+    // Directly replace full message body for single SMS.
+    if (!(aSegment.segmentMaxSeq && (aSegment.segmentMaxSeq > 1))) {
+      if (aSegment.encoding == Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET) {
+        aSegment.fullData = aSegment.data;
+      } else {
+        aSegment.fullBody = aSegment.body;
+      }
+      return aSegment;
+    }
+
+    // Handle Concatenation for Class 0 SMS
+    let hash = aSegment.sender + ":" +
+               aSegment.segmentRef + ":" +
+               aSegment.segmentMaxSeq;
+    let seq = aSegment.segmentSeq;
+
+    let options = this._receivedSmsSegmentsMap[hash];
+    if (!options) {
+      options = aSegment;
+      this._receivedSmsSegmentsMap[hash] = options;
+
+      options.receivedSegments = 0;
+      options.segments = [];
+    } else if (options.segments[seq]) {
+      // Duplicated segment?
+      if (DEBUG) {
+        debug("Got duplicated segment no." + seq +
+              " of a multipart SMS: " + JSON.stringify(aSegment));
+      }
+      return null;
+    }
+
+    if (options.receivedSegments > 0) {
+      // Update received timestamp.
+      options.timestamp = aSegment.timestamp;
+    }
+
+    if (options.encoding == Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET) {
+      options.segments[seq] = aSegment.data;
+    } else {
+      options.segments[seq] = aSegment.body;
+    }
+    options.receivedSegments++;
+
+    // The port information is only available in 1st segment for CDMA WAP Push.
+    // If the segments of a WAP Push are not received in sequence
+    // (e.g., SMS with seq == 1 is not the 1st segment received by the device),
+    // we have to retrieve the port information from 1st segment and
+    // save it into the cached options.
+    if (aSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
+        && seq === 1) {
+      if (options.originatorPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID
+          && aSegment.originatorPort !== Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
+        options.originatorPort = aSegment.originatorPort;
+      }
+
+      if (options.destinationPort === Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID
+          && aSegment.destinationPort !== Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
+        options.destinationPort = aSegment.destinationPort;
+      }
+    }
+
+    if (options.receivedSegments < options.segmentMaxSeq) {
+      if (DEBUG) {
+        debug("Got segment no." + seq + " of a multipart SMS: " +
+                           JSON.stringify(options));
+      }
+      return null;
+    }
+
+    // Remove from map
+    delete this._receivedSmsSegmentsMap[hash];
+
+    // Rebuild full body
+    if (options.encoding == Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET) {
+      // Uint8Array doesn't have `concat`, so we have to merge all segements
+      // by hand.
+      let fullDataLen = 0;
+      for (let i = 1; i <= options.segmentMaxSeq; i++) {
+        fullDataLen += options.segments[i].length;
+      }
+
+      options.fullData = new Uint8Array(fullDataLen);
+      for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
+        let data = options.segments[i];
+        for (let j = 0; j < data.length; j++) {
+          options.fullData[d++] = data[j];
+        }
+      }
+    } else {
+      options.fullBody = options.segments.join("");
+    }
+
+    // Remove handy fields after completing the concatenation.
+    delete options.receivedSegments;
+    delete options.segments;
+
+    if (DEBUG) {
+      debug("Got full multipart SMS: " + JSON.stringify(options));
+    }
+
+    return options;
+  },
+
+  /**
+   * Helper to purge complete message.
+   *
+   * We remove unnessary fields after completing the concatenation.
+   */
+  _purgeCompleteSmsMessage: function(aMessage) {
+    // Purge concatenation info
+    delete aMessage.segmentRef;
+    delete aMessage.segmentSeq;
+    delete aMessage.segmentMaxSeq;
+
+    // Purge partial message body
+    delete aMessage.data;
+    delete aMessage.body;
+  },
+
+  /**
+   * Handle WDP port push PDU. Constructor WDP bearer information and deliver
+   * to WapPushManager.
+   *
+   * @param aMessage
+   *        A SMS message.
+   */
+  _handleSmsWdpPortPush: function(aMessage, aServiceId) {
+    if (aMessage.encoding != Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET) {
+      if (DEBUG) {
+        debug("Got port addressed SMS but not encoded in 8-bit alphabet." +
+                   " Drop!");
+      }
+      return;
+    }
+
+    let options = {
+      bearer: gWAP.WDP_BEARER_GSM_SMS_GSM_MSISDN,
+      sourceAddress: aMessage.sender,
+      sourcePort: aMessage.originatorPort,
+      destinationAddress: this._getPhoneNumber(aServiceId),
+      destinationPort: aMessage.destinationPort,
+      serviceId: aServiceId
+    };
+    gWAP.WapPushManager.receiveWdpPDU(aMessage.fullData, aMessage.fullData.length,
+                                     0, options);
+  },
+
+  _handleCellbroadcastMessageReceived: function(aMessage, aServiceId) {
+    gCellBroadcastService
+      .notifyMessageReceived(aServiceId,
+                             Ci.nsICellBroadcastService.GSM_GEOGRAPHICAL_SCOPE_INVALID,
+                             aMessage.messageCode,
+                             aMessage.messageId,
+                             aMessage.language,
+                             aMessage.fullBody,
+                             Ci.nsICellBroadcastService.GSM_MESSAGE_CLASS_NORMAL,
+                             Date.now(),
+                             aMessage.serviceCategory,
+                             false,
+                             Ci.nsICellBroadcastService.GSM_ETWS_WARNING_INVALID,
+                             false,
+                             false);
+  },
+
+  _handleMwis: function(aMwi, aServiceId) {
+    let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
+                  .getService(Ci.nsIGonkVoicemailService);
+    service.notifyStatusChanged(aServiceId, aMwi.active, aMwi.msgCount,
+                                aMwi.returnNumber, aMwi.returnMessage);
+
+    gRadioInterfaces[aServiceId].sendWorkerMessage("updateMwis", { mwi: aMwi });
+  },
+
+  _portAddressedSmsApps: null,
+  _handleSmsReceived: function(aMessage, aServiceId) {
+    if (DEBUG) debug("_handleSmsReceived: " + JSON.stringify(aMessage));
+
+    if (aMessage.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
+      this._handleCellbroadcastMessageReceived(aMessage, aServiceId);
+      return true;
+    }
+
+    // Dispatch to registered handler if application port addressing is
+    // available. Note that the destination port can possibly be zero when
+    // representing a UDP/TCP port.
+    if (aMessage.destinationPort !== Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID) {
+      let handler = this._portAddressedSmsApps[aMessage.destinationPort];
+      if (handler) {
+        handler(aMessage, aServiceId);
+      }
+      return true;
+    }
+
+    if (aMessage.encoding == Ci.nsIGonkSmsService.SMS_MESSAGE_ENCODING_8BITS_ALPHABET) {
+      // Don't know how to handle binary data yet.
+      return true;
+    }
+
+    aMessage.type = "sms";
+    aMessage.sender = aMessage.sender || null;
+    aMessage.receiver = this._getPhoneNumber(aServiceId);
+    aMessage.body = aMessage.fullBody = aMessage.fullBody || null;
+
+    if (this._isSilentNumber(aMessage.sender)) {
+      aMessage.id = -1;
+      aMessage.threadId = 0;
+      aMessage.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
+      aMessage.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
+      aMessage.read = false;
+
+      let domMessage =
+        gMobileMessageService.createSmsMessage(aMessage.id,
+                                               aMessage.threadId,
+                                               aMessage.iccId,
+                                               aMessage.delivery,
+                                               aMessage.deliveryStatus,
+                                               aMessage.sender,
+                                               aMessage.receiver,
+                                               aMessage.body,
+                                               aMessage.messageClass,
+                                               aMessage.timestamp,
+                                               aMessage.sentTimestamp,
+                                               0,
+                                               aMessage.read);
+
+      Services.obs.notifyObservers(domMessage,
+                                   kSilentSmsReceivedObserverTopic,
+                                   null);
+      return true;
+    }
+
+    if (aMessage.mwiPresent) {
+      let mwi = {
+        discard: aMessage.mwiDiscard,
+        msgCount: aMessage.mwiMsgCount,
+        active: aMessage.mwiActive,
+        returnNumber: aMessage.sender || null,
+        returnMessage: aMessage.fullBody || null
+      };
+
+      this._handleMwis(mwi, aServiceId);
+
+      // Dicarded MWI comes without text body.
+      // Hence, we discard it here after notifying the MWI status.
+      if (aMessage.mwiDiscard) {
+        return true;
+      }
+    }
+
+    let notifyReceived = (aRv, aDomMessage) => {
+      let success = Components.isSuccessCode(aRv);
+
+      this._sendAckSms(aRv, aMessage, aServiceId);
+
+      if (!success) {
+        // At this point we could send a message to content to notify the user
+        // that storing an incoming SMS failed, most likely due to a full disk.
+        if (DEBUG) {
+          debug("Could not store SMS, error code " + aRv);
+        }
+        return;
+      }
+
+      this._broadcastSmsSystemMessage(
+        Ci.nsISmsMessenger.NOTIFICATION_TYPE_RECEIVED, aDomMessage);
+      Services.obs.notifyObservers(aDomMessage, kSmsReceivedObserverTopic, null);
+    };
+
+    if (aMessage.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
+      gMobileMessageDatabaseService.saveReceivedMessage(aMessage,
+                                                        notifyReceived);
+    } else {
+      aMessage.id = -1;
+      aMessage.threadId = 0;
+      aMessage.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
+      aMessage.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
+      aMessage.read = false;
+
+      let domMessage =
+        gMobileMessageService.createSmsMessage(aMessage.id,
+                                               aMessage.threadId,
+                                               aMessage.iccId,
+                                               aMessage.delivery,
+                                               aMessage.deliveryStatus,
+                                               aMessage.sender,
+                                               aMessage.receiver,
+                                               aMessage.body,
+                                               aMessage.messageClass,
+                                               aMessage.timestamp,
+                                               aMessage.sentTimestamp,
+                                               0,
+                                               aMessage.read);
+
+      notifyReceived(Cr.NS_OK, domMessage);
+    }
+
+    // SMS ACK will be sent in notifyReceived. Return false here.
+    return false;
+  },
+
+  /**
+   * Handle ACK response of received SMS.
+   */
+  _sendAckSms: function(aRv, aMessage, aServiceId) {
+    if (aMessage.messageClass === RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_2]) {
+      return;
+    }
+
+    let result = RIL.PDU_FCS_OK;
+    if (!Components.isSuccessCode(aRv)) {
+      if (DEBUG) debug("Failed to handle received sms: " + aRv);
+      result = (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE)
+                ? RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED
+                : RIL.PDU_FCS_UNSPECIFIED;
+    }
+
+    gRadioInterfaces[aServiceId]
+      .sendWorkerMessage("ackSMS", { result: result });
+
+  },
+
+  // An array of slient numbers.
+  _silentNumbers: null,
+  _isSilentNumber: function(aNumber) {
+    return this._silentNumbers.indexOf(aNumber) >= 0;
+  },
+
+  /**
    * nsISmsService interface
    */
   smsDefaultServiceId: 0,
 
   getSegmentInfoForText: function(aText, aRequest) {
     let strict7BitEncoding;
     try {
       strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
@@ -502,24 +901,18 @@ SmsService.prototype = {
       saveSendingMessageCallback(Cr.NS_OK, domMessage);
       return;
     }
 
     gMobileMessageDatabaseService.saveSendingMessage(
       sendingMessage, saveSendingMessageCallback);
   },
 
-  // An array of slient numbers.
-  _silentNumbers: null,
-  isSilentNumber: function(aNumber) {
-    return this._silentNumbers.indexOf(aNumber) >= 0;
-  },
-
   addSilentNumber: function(aNumber) {
-    if (this.isSilentNumber(aNumber)) {
+    if (this._isSilentNumber(aNumber)) {
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
     this._silentNumbers.push(aNumber);
   },
 
   removeSilentNumber: function(aNumber) {
    let index = this._silentNumbers.indexOf(aNumber);
@@ -543,33 +936,107 @@ SmsService.prototype = {
       } else {
         aRequest.notifyGetSmscAddressFailed(
           Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
       }
     });
   },
 
   /**
-   * TODO: nsIGonkSmsService interface
+   * nsIGonkSmsService interface
    */
+  notifyMessageReceived: function(aServiceId, aSMSC, aSentTimestamp,
+                                  aSender, aPid, aEncoding, aMessageClass,
+                                  aLanguage, aSegmentRef, aSegmentSeq,
+                                  aSegmentMaxSeq, aOriginatorPort,
+                                  aDestinationPort, aMwiPresent, aMwiDiscard,
+                                  aMwiMsgCount, aMwiActive, aCdmaMessageType,
+                                  aCdmaTeleservice, aCdmaServiceCategory,
+                                  aBody, aData, aDataLength) {
+
+    this._acquireSmsHandledWakeLock();
+
+    let segment = {};
+    segment.iccId = this._getIccId(aServiceId);
+    segment.SMSC = aSMSC;
+    segment.sentTimestamp = aSentTimestamp;
+    segment.timestamp = Date.now();
+    segment.sender = aSender;
+    segment.pid = aPid;
+    segment.encoding = aEncoding;
+    segment.messageClass = this._convertSmsMessageClassToString(aMessageClass);
+    segment.language = aLanguage;
+    segment.segmentRef = aSegmentRef;
+    segment.segmentSeq = aSegmentSeq;
+    segment.segmentMaxSeq = aSegmentMaxSeq;
+    segment.originatorPort = aOriginatorPort;
+    segment.destinationPort = aDestinationPort;
+    segment.mwiPresent = aMwiPresent;
+    segment.mwiDiscard = aMwiDiscard;
+    segment.mwiMsgCount = aMwiMsgCount;
+    segment.mwiActive = aMwiActive;
+    segment.messageType = aCdmaMessageType;
+    segment.teleservice = aCdmaTeleservice;
+    segment.serviceCategory = aCdmaServiceCategory;
+    segment.body = aBody;
+    segment.data = (aData && aDataLength > 0) ? aData : null;
+
+    let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
+    let messageClass = segment.messageClass;
+
+    let handleReceivedAndAck = (aRvOfIncompleteMsg, aCompleteMessage) => {
+      if (aCompleteMessage) {
+        this._purgeCompleteSmsMessage(aCompleteMessage);
+        if (this._handleSmsReceived(aCompleteMessage, aServiceId)) {
+          this._sendAckSms(Cr.NS_OK, aCompleteMessage, aServiceId);
+        }
+        // else Ack will be sent after further process in _handleSmsReceived.
+      } else {
+        this._sendAckSms(aRvOfIncompleteMsg, segment, aServiceId);
+      }
+    };
+
+    // No need to access SmsSegmentStore for Class 0 SMS and Single SMS.
+    if (!isMultipart ||
+        (messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0])) {
+      // `When a mobile terminated message is class 0 and the MS has the
+      // capability of displaying short messages, the MS shall display the
+      // message immediately and send an acknowledgement to the SC when the
+      // message has successfully reached the MS irrespective of whether
+      // there is memory available in the (U)SIM or ME. The message shall
+      // not be automatically stored in the (U)SIM or ME.`
+      // ~ 3GPP 23.038 clause 4
+
+      handleReceivedAndAck(Cr.NS_OK,  // ACK OK For Incomplete Class 0
+                           this._processReceivedSmsSegment(segment));
+    } else {
+      gMobileMessageDatabaseService
+        .saveSmsSegment(segment, function notifyResult(aRv, aCompleteMessage) {
+        handleReceivedAndAck(aRv,  // Ack according to the result after saving
+                             aCompleteMessage);
+      });
+    }
+  },
 
   /**
    * nsIObserver interface.
    */
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
         if (aData === kPrefRilDebuggingEnabled) {
           this._updateDebugFlag();
         }
         else if (aData === kPrefDefaultServiceId) {
           this.smsDefaultServiceId = this._getDefaultServiceId();
         }
         break;
       case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
+        // Release the CPU wake lock for handling the received SMS.
+        this._releaseSmsHandledWakeLock();
         Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
         Services.prefs.removeObserver(kPrefDefaultServiceId, this);
         Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
         break;
     }
   }
 };
 
--- a/dom/mobilemessage/interfaces/nsIGonkSmsService.idl
+++ b/dom/mobilemessage/interfaces/nsIGonkSmsService.idl
@@ -1,18 +1,95 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "domstubs.idl"
 #include "nsISmsService.idl"
 
 %{C++
 #define GONK_SMSSERVICE_CONTRACTID \
         "@mozilla.org/sms/gonksmsservice;1"
 %}
 
-[scriptable, uuid(63fab75e-73b4-11e4-a10d-dbfa9d05a4f4)]
+[scriptable, uuid(4dda515e-05ec-47b1-b750-e42c74576c43)]
 interface nsIGonkSmsService : nsISmsService
 {
+  const unsigned short SMS_MESSAGE_ENCODING_7BITS_ALPHABET = 0x00;
+  const unsigned short SMS_MESSAGE_ENCODING_8BITS_ALPHABET = 0x04;
+  const unsigned short SMS_MESSAGE_ENCODING_16BITS_ALPHABET = 0x08;
+
+  const unsigned long SMS_APPLICATION_PORT_INVALID = 0xFFFFFFFF;
+
   /**
-   * TODO: define callback to receive message from the network.
+   * Called when a short message has been received by the network.
+   *
+   * @param aServiceId
+   *        The ID of the service where this message is received from.
+   * @param aSMSC
+   *        SMS Center address.
+   * @param aSentTimestamp
+   *        The time stamp when message is arrived to SMSC.
+   * @param aSender
+   *        The sender's address of this message.
+   * @param aPid
+   *        Protocol Identifier, See TS 23.040, subcluase 9.2.3.9.
+   *        Set to 0 if invalid.
+   * @param aEncoding
+   *        The encoding of message body.
+   *        SMS_MESSAGE_ENCODING_*.
+   * @param aMessageClass
+   *        A predefined constant of nsISmsService.MESSAGE_CLASS_TYPE_*.
+   * @param aLanguage
+   *        ISO-639-1 language code for this message. Null if unspecified.
+   * @param aSegmentRef, aSegmentSeq, aSegmentMaxSeq
+   *        Concatenation info. See TS 23.040, subclause 9.2.3.24.1.
+   *        All set to 1 if no need for concatenatenation.
+   * @param aOriginatorPort, aDestinationPort
+   *        Application Port Addressing. See TS 23.040 subclause 9.2.3.24.3~4.
+   *        All set to 1 if no need for concatenatenation.
+   * @param aMwiPresent
+   *        True if MWI is presented in this message.
+   * @param aMwiDiscard
+   *        True if MWI has to be discarded after received.
+   * @param aMwiMsgCount
+   *        The number of messages waiting in the voicemail server.
+   *        -1 if number is unknown from the decoded MWI.
+   * @param aMwiActive
+   *        True if there are messages waiting in the voicemail server.
+   * @param aCdmaMessageType
+   *        CDMA SMS Message Type, as defined in 3GPP2 C.S0015-A v2.0, Table 3.4-1
+   *        Set to 0 if invalid.
+   * @param aCdmaTeleservice
+   *        SMS Teleservice Identitifier, as defined in 3GPP2 N.S0005, Table 175.
+   *        Set to 0 if invalid.
+   * @param aCdmaServiceCategory
+   *        CDMA Service Category, 3GPP2 C.R1001-D v2.0, 9.3 Service Category.
+   *        Set to 0 if invalid.
+   * @param aBody
+   *        Text message body.
+   * @param aData
+   *        Binary message body.
    */
-};
\ No newline at end of file
+  void notifyMessageReceived(in unsigned long aServiceId,
+                             in DOMString aSMSC,
+                             in DOMTimeStamp aSentTimestamp,
+                             in DOMString aSender,
+                             in unsigned short aPid,
+                             in unsigned short aEncoding,
+                             in unsigned long aMessageClass,
+                             in DOMString aLanguage,
+                             in unsigned short aSegmentRef,
+                             in unsigned short aSegmentSeq,
+                             in unsigned short aSegmentMaxSeq,
+                             in unsigned long aOriginatorPort,
+                             in unsigned long aDestinationPort,
+                             in boolean aMwiPresent,
+                             in boolean aMwiDiscard,
+                             in short aMwiMsgCount,
+                             in boolean aMwiActive,
+                             in unsigned short aCdmaMessageType,
+                             in unsigned long aCdmaTeleservice,
+                             in unsigned long aCdmaServiceCategory,
+                             in DOMString aBody,
+                             [array, size_is(aDataLength)] in octet aData,
+                             in uint32_t aDataLength);
+};
--- a/dom/mobilemessage/interfaces/nsISmsService.idl
+++ b/dom/mobilemessage/interfaces/nsISmsService.idl
@@ -7,17 +7,17 @@
 interface nsIDOMMozSmsMessage;
 interface nsIMobileMessageCallback;
 
 %{C++
 #define SMS_SERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } }
 #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
 %}
 
-[scriptable, uuid(31626940-73b4-11e4-8b03-1724e1d8a6a1)]
+[scriptable, uuid(ae688bca-00c9-4d08-945d-e8a5272ad5b1)]
 interface nsISmsService : nsISupports
 {
   /**
    * Constant definitions of predefined GSM Message Class
    * See 3GPP TS 23.038 clause 4 SMS Data Coding Scheme
    */
   const unsigned short MESSAGE_CLASS_TYPE_CLASS_0 = 0;
   const unsigned short MESSAGE_CLASS_TYPE_CLASS_1 = 1;
@@ -47,17 +47,16 @@ interface nsISmsService : nsISupports
                              in nsIMobileMessageCallback request);
 
   void send(in unsigned long serviceId,
             in DOMString number,
             in DOMString message,
             in boolean silent,
             in nsIMobileMessageCallback request);
 
-  boolean isSilentNumber(in DOMString number);
   void addSilentNumber(in DOMString number);
   void removeSilentNumber(in DOMString number);
 
   void getSmscAddress(in unsigned long serviceId,
                       in nsIMobileMessageCallback request);
 };
 
 %{C++
--- a/dom/mobilemessage/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/ipc/SmsIPCService.cpp
@@ -189,24 +189,16 @@ SmsIPCService::Send(uint32_t aServiceId,
   return SendRequest(SendMessageRequest(SendSmsMessageRequest(aServiceId,
                                                               nsString(aNumber),
                                                               nsString(aMessage),
                                                               aSilent)),
                      aRequest);
 }
 
 NS_IMETHODIMP
-SmsIPCService::IsSilentNumber(const nsAString& aNumber,
-                              bool*            aIsSilent)
-{
-  NS_ERROR("We should not be here!");
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
 SmsIPCService::AddSilentNumber(const nsAString& aNumber)
 {
   PSmsChild* smsChild = GetSmsChild();
   NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE);
 
   smsChild->SendAddSilentNumber(nsString(aNumber));
   return NS_OK;
 }
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -51,45 +51,32 @@ const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 const RADIOINTERFACE_CID =
   Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
 const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 const kNetworkConnStateChangedTopic      = "network-connection-state-changed";
-const kSmsReceivedObserverTopic          = "sms-received";
-const kSilentSmsReceivedObserverTopic    = "silent-sms-received";
-const kSmsSendingObserverTopic           = "sms-sending";
-const kSmsSentObserverTopic              = "sms-sent";
-const kSmsFailedObserverTopic            = "sms-failed";
-const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
-const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 const kSysMsgListenerReadyObserverTopic  = "system-message-listener-ready";
 const kSysClockChangeObserverTopic       = "system-clock-change";
 const kScreenStateChangedTopic           = "screen-state-changed";
 
 const kSettingsClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
 const kSettingsClockAutoUpdateAvailable = "time.clock.automatic-update.available";
 const kSettingsTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
 const kSettingsTimezoneAutoUpdateAvailable = "time.timezone.automatic-update.available";
 
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
 
 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
 const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
 
-const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
-const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
-const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
-const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
-
 const RADIO_POWER_OFF_TIMEOUT = 30000;
-const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
 const HW_DEFAULT_CLIENT_ID = 0;
 
 const INT32_MAX = 2147483647;
 
 const NETWORK_TYPE_UNKNOWN     = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
 const NETWORK_TYPE_WIFI        = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NETWORK_TYPE_MOBILE      = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
 const NETWORK_TYPE_MOBILE_MMS  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS;
@@ -131,44 +118,32 @@ function updateDebugFlag() {
   DEBUG = RIL.DEBUG_RIL || debugPref;
 }
 updateDebugFlag();
 
 function debug(s) {
   dump("-*- RadioInterfaceLayer: " + s + "\n");
 }
 
-XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
-                                   "@mozilla.org/power/powermanagerservice;1",
-                                   "nsIPowerManagerService");
-
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
                                    "nsIMobileMessageService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/gonksmsservice;1",
                                    "nsIGonkSmsService");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
-                                   "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
-                                   "nsIRilMobileMessageDatabaseService");
-
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
-                                   "@mozilla.org/system-message-internal;1",
-                                   "nsISystemMessagesInternal");
-
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gTimeService",
                                    "@mozilla.org/time/timeservice;1",
                                    "nsITimeService");
 
@@ -183,36 +158,26 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIGonkMobileConnectionService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
                                    "@mozilla.org/cellbroadcast/gonkservice;1",
                                    "nsIGonkCellBroadcastService");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gSmsMessenger",
-                                   "@mozilla.org/ril/system-messenger-helper;1",
-                                   "nsISmsMessenger");
-
 XPCOMUtils.defineLazyServiceGetter(this, "gIccMessenger",
                                    "@mozilla.org/ril/system-messenger-helper;1",
                                    "nsIIccMessenger");
 
 XPCOMUtils.defineLazyGetter(this, "gStkCmdFactory", function() {
   let stk = {};
   Cu.import("resource://gre/modules/StkProactiveCmdFactory.jsm", stk);
   return stk.StkProactiveCmdFactory;
 });
 
-XPCOMUtils.defineLazyGetter(this, "WAP", function() {
-  let wap = {};
-  Cu.import("resource://gre/modules/WapPushManager.js", wap);
-  return wap;
-});
-
 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function() {
   return {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                            Ci.nsIObserver]),
 
     ril: null,
 
     // Manage message targets in terms of topic. Only the authorized and
@@ -1735,21 +1700,16 @@ function RadioInterface(aClientId, aWork
   this.setTimezoneAutoUpdateAvailable(false);
 
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
   Services.obs.addObserver(this, kScreenStateChangedTopic, false);
 
   Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
 
-  this.portAddressedSmsApps = {};
-  this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
-
-  this._receivedSmsSegmentsMap = {};
-
   this._sntp = new Sntp(this.setClockBySntp.bind(this),
                         Services.prefs.getIntPref("network.sntp.maxRetryCount"),
                         Services.prefs.getIntPref("network.sntp.refreshPeriod"),
                         Services.prefs.getIntPref("network.sntp.timeout"),
                         Services.prefs.getCharPref("network.sntp.pools").split(";"),
                         Services.prefs.getIntPref("network.sntp.port"));
 }
 
@@ -1767,19 +1727,16 @@ RadioInterface.prototype = {
   // A private wrapped WorkerMessenger instance.
   workerMessenger: null,
 
   debug: function(s) {
     dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n");
   },
 
   shutdown: function() {
-    // Release the CPU wake lock for handling the received SMS.
-    this._releaseSmsHandledWakeLock();
-
     Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
     Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
     Services.obs.removeObserver(this, kScreenStateChangedTopic);
     Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
   },
 
   /**
    * A utility function to copy objects. The srcInfo may contain
@@ -1972,17 +1929,17 @@ RadioInterface.prototype = {
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
         gRadioEnabledController.receiveCardState(this.clientId);
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
-        this.handleSmsMultipart(message);
+        this.handleSmsReceived(message);
         break;
       case "cellbroadcast-received":
         this.handleCellbroadcastMessageReceived(message);
         break;
       case "nitzTime":
         this.handleNitzTime(message);
         break;
       case "iccinfochange":
@@ -2007,68 +1964,16 @@ RadioInterface.prototype = {
         this.handleCdmaInformationRecords(message.records);
         break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
-  /**
-   * Get phone number from iccInfo.
-   *
-   * If the icc card is gsm card, the phone number is in msisdn.
-   * @see nsIGsmIccInfo
-   *
-   * Otherwise, the phone number is in mdn.
-   * @see nsICdmaIccInfo
-   */
-  getPhoneNumber: function() {
-    let iccInfo = this.rilContext.iccInfo;
-
-    if (!iccInfo) {
-      return null;
-    }
-
-    // After moving SMS code out of RadioInterfaceLayer, we could use
-    // |iccInfo instanceof Ci.nsIGsmIccInfo| here.
-    // TODO: Bug 873351 - B2G SMS: move SMS code out of RadioInterfaceLayer to
-    //                    SmsService
-    let number = (iccInfo instanceof GsmIccInfo) ? iccInfo.msisdn : iccInfo.mdn;
-
-    // Workaround an xpconnect issue with undefined string objects.
-    // See bug 808220
-    if (number === undefined || number === "undefined") {
-      return null;
-    }
-
-    return number;
-  },
-
-  /**
-   * A utility function to get the ICC ID of the SIM card (if installed).
-   */
-  getIccId: function() {
-    let iccInfo = this.rilContext.iccInfo;
-
-    if (!iccInfo) {
-      return null;
-    }
-
-    let iccId = iccInfo.iccid;
-
-    // Workaround an xpconnect issue with undefined string objects.
-    // See bug 808220
-    if (iccId === undefined || iccId === "undefined") {
-      return null;
-    }
-
-    return iccId;
-  },
-
   // Matches the mvnoData pattern with imsi. Characters 'x' and 'X' are skipped
   // and not compared. E.g., if the mvnoData passed is '310260x10xxxxxx',
   // then the function returns true only if imsi has the same first 6 digits,
   // 8th and 9th digit.
   isImsiMatches: function(mvnoData) {
     let imsi = this.rilContext.imsi;
 
     // This should not be an error, but a mismatch.
@@ -2158,516 +2063,74 @@ RadioInterface.prototype = {
    *                    NetworkManager
    */
   updateRILNetworkInterface: function() {
     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
     connHandler.updateRILNetworkInterface();
   },
 
   /**
-   * Handle WDP port push PDU. Constructor WDP bearer information and deliver
-   * to WapPushManager.
-   *
-   * @param message
-   *        A SMS message.
-   */
-  handleSmsWdpPortPush: function(message) {
-    if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      if (DEBUG) {
-        this.debug("Got port addressed SMS but not encoded in 8-bit alphabet." +
-                   " Drop!");
-      }
-      return;
-    }
-
-    let options = {
-      bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN,
-      sourceAddress: message.sender,
-      sourcePort: message.originatorPort,
-      destinationAddress: this.rilContext.iccInfo.msisdn,
-      destinationPort: message.destinationPort,
-      serviceId: this.clientId
-    };
-    WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
-                                     0, options);
-  },
-
-  _convertSmsMessageClass: function(aMessageClass) {
-    let index = RIL.GECKO_SMS_MESSAGE_CLASSES.indexOf(aMessageClass);
-
-    if (index < 0) {
-      throw new Error("Invalid MessageClass: " + aMessageClass);
-    }
-
-    return index;
-  },
-
-  _convertSmsDelivery: function(aDelivery) {
-    let index = [DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED,
-                 DOM_MOBILE_MESSAGE_DELIVERY_SENDING,
-                 DOM_MOBILE_MESSAGE_DELIVERY_SENT,
-                 DOM_MOBILE_MESSAGE_DELIVERY_ERROR].indexOf(aDelivery);
-
-    if (index < 0) {
-      throw new Error("Invalid Delivery: " + aDelivery);
-    }
-
-    return index;
-  },
-
-  _convertSmsDeliveryStatus: function(aDeliveryStatus) {
-    let index = [RIL.GECKO_SMS_DELIVERY_STATUS_NOT_APPLICABLE,
-                 RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS,
-                 RIL.GECKO_SMS_DELIVERY_STATUS_PENDING,
-                 RIL.GECKO_SMS_DELIVERY_STATUS_ERROR].indexOf(aDeliveryStatus);
-
-    if (index < 0) {
-      throw new Error("Invalid DeliveryStatus: " + aDeliveryStatus);
-    }
-
-    return index;
-  },
-
-  /**
-   * A helper to broadcast the system message to launch registered apps
-   * like Costcontrol, Notification and Message app... etc.
-   *
-   * @param aName
-   *        The system message name.
-   * @param aDomMessage
-   *        The nsIDOMMozSmsMessage object.
-   */
-  broadcastSmsSystemMessage: function(aNotificationType, aDomMessage) {
-    if (DEBUG) this.debug("Broadcasting the SMS system message: " + aNotificationType);
-
-    // Sadly we cannot directly broadcast the aDomMessage object
-    // because the system message mechamism will rewrap the object
-    // based on the content window, which needs to know the properties.
-    try {
-      gSmsMessenger.notifySms(aNotificationType,
-                              aDomMessage.id,
-                              aDomMessage.threadId,
-                              aDomMessage.iccId,
-                              this._convertSmsDelivery(
-                                aDomMessage.delivery),
-                              this._convertSmsDeliveryStatus(
-                                aDomMessage.deliveryStatus),
-                              aDomMessage.sender,
-                              aDomMessage.receiver,
-                              aDomMessage.body,
-                              this._convertSmsMessageClass(
-                                aDomMessage.messageClass),
-                              aDomMessage.timestamp,
-                              aDomMessage.sentTimestamp,
-                              aDomMessage.deliveryTimestamp,
-                              aDomMessage.read);
-    } catch (e) {
-      if (DEBUG) {
-        this.debug("Failed to broadcastSmsSystemMessage: " + e);
-      }
-    }
-  },
-
-  // The following attributes/functions are used for acquiring/releasing the
-  // CPU wake lock when the RIL handles the received SMS. Note that we need
-  // a timer to bound the lock's life cycle to avoid exhausting the battery.
-  _smsHandledWakeLock: null,
-  _smsHandledWakeLockTimer: null,
-
-  _acquireSmsHandledWakeLock: function() {
-    if (!this._smsHandledWakeLock) {
-      if (DEBUG) this.debug("Acquiring a CPU wake lock for handling SMS.");
-      this._smsHandledWakeLock = gPowerManagerService.newWakeLock("cpu");
-    }
-    if (!this._smsHandledWakeLockTimer) {
-      if (DEBUG) this.debug("Creating a timer for releasing the CPU wake lock.");
-      this._smsHandledWakeLockTimer =
-        Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    }
-    if (DEBUG) this.debug("Setting the timer for releasing the CPU wake lock.");
-    this._smsHandledWakeLockTimer
-        .initWithCallback(this._releaseSmsHandledWakeLock.bind(this),
-                          SMS_HANDLED_WAKELOCK_TIMEOUT,
-                          Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  _releaseSmsHandledWakeLock: function() {
-    if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
-    if (this._smsHandledWakeLockTimer) {
-      this._smsHandledWakeLockTimer.cancel();
-    }
-    if (this._smsHandledWakeLock) {
-      this._smsHandledWakeLock.unlock();
-      this._smsHandledWakeLock = null;
-    }
-  },
-
-  /**
-   * 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: null,
-
-  /**
-   * Helper for processing received multipart SMS.
-   *
-   * @return null for handled segments, and an object containing full message
-   *         body/data once all segments are received.
+   * handle received SMS.
    */
-  _processReceivedSmsSegment: function(aSegment) {
-
-    // Directly replace full message body for single SMS.
-    if (!(aSegment.segmentMaxSeq && (aSegment.segmentMaxSeq > 1))) {
-      if (aSegment.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-        aSegment.fullData = aSegment.data;
-      } else {
-        aSegment.fullBody = aSegment.body;
-      }
-      return aSegment;
-    }
-
-    // Handle Concatenation for Class 0 SMS
-    let hash = aSegment.sender + ":" +
-               aSegment.segmentRef + ":" +
-               aSegment.segmentMaxSeq;
-    let seq = aSegment.segmentSeq;
-
-    let options = this._receivedSmsSegmentsMap[hash];
-    if (!options) {
-      options = aSegment;
-      this._receivedSmsSegmentsMap[hash] = options;
-
-      options.receivedSegments = 0;
-      options.segments = [];
-    } else if (options.segments[seq]) {
-      // Duplicated segment?
-      if (DEBUG) {
-        this.debug("Got duplicated segment no." + seq +
-                           " of a multipart SMS: " + JSON.stringify(aSegment));
-      }
-      return null;
-    }
-
-    if (options.receivedSegments > 0) {
-      // Update received timestamp.
-      options.timestamp = aSegment.timestamp;
-    }
-
-    if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      options.segments[seq] = aSegment.data;
-    } else {
-      options.segments[seq] = aSegment.body;
-    }
-    options.receivedSegments++;
-
-    // The port information is only available in 1st segment for CDMA WAP Push.
-    // If the segments of a WAP Push are not received in sequence
-    // (e.g., SMS with seq == 1 is not the 1st segment received by the device),
-    // we have to retrieve the port information from 1st segment and
-    // save it into the cached options.
-    if (aSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
-        && seq === 1) {
-      if (!options.originatorPort && aSegment.originatorPort) {
-        options.originatorPort = aSegment.originatorPort;
-      }
-
-      if (!options.destinationPort && aSegment.destinationPort) {
-        options.destinationPort = aSegment.destinationPort;
-      }
-    }
-
-    if (options.receivedSegments < options.segmentMaxSeq) {
-      if (DEBUG) {
-        this.debug("Got segment no." + seq + " of a multipart SMS: " +
-                           JSON.stringify(options));
-      }
-      return null;
-    }
-
-    // Remove from map
-    delete this._receivedSmsSegmentsMap[hash];
-
-    // Rebuild full body
-    if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      // Uint8Array doesn't have `concat`, so we have to merge all segements
-      // by hand.
-      let fullDataLen = 0;
-      for (let i = 1; i <= options.segmentMaxSeq; i++) {
-        fullDataLen += options.segments[i].length;
-      }
-
-      options.fullData = new Uint8Array(fullDataLen);
-      for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
-        let data = options.segments[i];
-        for (let j = 0; j < data.length; j++) {
-          options.fullData[d++] = data[j];
-        }
-      }
-    } else {
-      options.fullBody = options.segments.join("");
-    }
-
-    // Remove handy fields after completing the concatenation.
-    delete options.receivedSegments;
-    delete options.segments;
-
-    if (DEBUG) {
-      this.debug("Got full multipart SMS: " + JSON.stringify(options));
-    }
-
-    return options;
-  },
-
-  /**
-   * Helper to create Savable SmsSegment.
-   */
-  _createSavableSmsSegment: function(aMessage) {
-    // We precisely define what data fields to be stored into
-    // DB here for better data migration.
-    let segment = {};
-    segment.messageType = aMessage.messageType;
-    segment.teleservice = aMessage.teleservice;
-    segment.SMSC = aMessage.SMSC;
-    segment.sentTimestamp = aMessage.sentTimestamp;
-    segment.timestamp = Date.now();
-    segment.sender = aMessage.sender;
-    segment.pid = aMessage.pid;
-    segment.encoding = aMessage.encoding;
-    segment.messageClass = aMessage.messageClass;
-    segment.iccId = this.getIccId();
-    if (aMessage.header) {
-      segment.segmentRef = aMessage.header.segmentRef;
-      segment.segmentSeq = aMessage.header.segmentSeq;
-      segment.segmentMaxSeq = aMessage.header.segmentMaxSeq;
-      segment.originatorPort = aMessage.header.originatorPort;
-      segment.destinationPort = aMessage.header.destinationPort;
-    }
-    segment.mwiPresent = (aMessage.mwi)? true: false;
-    segment.mwiDiscard = (segment.mwiPresent)? aMessage.mwi.discard: false;
-    segment.mwiMsgCount = (segment.mwiPresent)? aMessage.mwi.msgCount: 0;
-    segment.mwiActive = (segment.mwiPresent)? aMessage.mwi.active: false;
-    segment.serviceCategory = aMessage.serviceCategory;
-    segment.language = aMessage.language;
-    segment.data = aMessage.data;
-    segment.body = aMessage.body;
-
-    return segment;
-  },
-
-  /**
-   * Helper to purge complete message.
-   *
-   * We remove unnessary fields defined in _createSavableSmsSegment() after
-   * completing the concatenation.
-   */
-  _purgeCompleteSmsMessage: function(aMessage) {
-    // Purge concatenation info
-    delete aMessage.segmentRef;
-    delete aMessage.segmentSeq;
-    delete aMessage.segmentMaxSeq;
-
-    // Purge partial message body
-    delete aMessage.data;
-    delete aMessage.body;
-  },
-
-  /**
-   * handle concatenation of received SMS.
-   */
-  handleSmsMultipart: function(aMessage) {
-    if (DEBUG) this.debug("handleSmsMultipart: " + JSON.stringify(aMessage));
-
-    this._acquireSmsHandledWakeLock();
-
-    let segment = this._createSavableSmsSegment(aMessage);
-
-    let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
-    let messageClass = segment.messageClass;
-
-    let handleReceivedAndAck = function(aRvOfIncompleteMsg, aCompleteMessage) {
-      if (aCompleteMessage) {
-        this._purgeCompleteSmsMessage(aCompleteMessage);
-        if (this.handleSmsReceived(aCompleteMessage)) {
-          this.sendAckSms(Cr.NS_OK, aCompleteMessage);
-        }
-        // else Ack will be sent after further process in handleSmsReceived.
-      } else {
-        this.sendAckSms(aRvOfIncompleteMsg, segment);
-      }
-    }.bind(this);
-
-    // No need to access SmsSegmentStore for Class 0 SMS and Single SMS.
-    if (!isMultipart ||
-        (messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0])) {
-      // `When a mobile terminated message is class 0 and the MS has the
-      // capability of displaying short messages, the MS shall display the
-      // message immediately and send an acknowledgement to the SC when the
-      // message has successfully reached the MS irrespective of whether
-      // there is memory available in the (U)SIM or ME. The message shall
-      // not be automatically stored in the (U)SIM or ME.`
-      // ~ 3GPP 23.038 clause 4
-
-      handleReceivedAndAck(Cr.NS_OK,  // ACK OK For Incomplete Class 0
-                           this._processReceivedSmsSegment(segment));
-    } else {
-      gMobileMessageDatabaseService
-        .saveSmsSegment(segment, function notifyResult(aRv, aCompleteMessage) {
-        handleReceivedAndAck(aRv,  // Ack according to the result after saving
-                             aCompleteMessage);
-      });
-    }
-  },
-
-  portAddressedSmsApps: null,
-  handleSmsReceived: function(message) {
-    if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
-
-    if (message.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
-      this.handleCellbroadcastMessageReceived(message);
-      return true;
-    }
-
-    // Dispatch to registered handler if application port addressing is
-    // available. Note that the destination port can possibly be zero when
-    // representing a UDP/TCP port.
-    if (message.destinationPort != null) {
-      let handler = this.portAddressedSmsApps[message.destinationPort];
-      if (handler) {
-        handler(message);
-      }
-      return true;
-    }
-
-    if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      // Don't know how to handle binary data yet.
-      return true;
-    }
-
-    message.type = "sms";
-    message.sender = message.sender || null;
-    message.receiver = this.getPhoneNumber();
-    message.body = message.fullBody = message.fullBody || null;
-
-    if (gSmsService.isSilentNumber(message.sender)) {
-      message.id = -1;
-      message.threadId = 0;
-      message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
-      message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
-      message.read = false;
-
-      let domMessage =
-        gMobileMessageService.createSmsMessage(message.id,
-                                               message.threadId,
-                                               message.iccId,
-                                               message.delivery,
-                                               message.deliveryStatus,
-                                               message.sender,
-                                               message.receiver,
-                                               message.body,
-                                               message.messageClass,
-                                               message.timestamp,
-                                               message.sentTimestamp,
-                                               0,
-                                               message.read);
-
-      Services.obs.notifyObservers(domMessage,
-                                   kSilentSmsReceivedObserverTopic,
-                                   null);
-      return true;
-    }
-
-    if (message.mwiPresent) {
-      let mwi = {
-        discard: message.mwiDiscard,
-        msgCount: message.mwiMsgCount,
-        active: message.mwiActive
-      };
-      this.workerMessenger.send("updateMwis", { mwi: mwi });
-
-      mwi.returnNumber = message.sender;
-      mwi.returnMessage = message.fullBody;
-      this.handleIccMwis(mwi);
-
-      // Dicarded MWI comes without text body.
-      // Hence, we discard it here after notifying the MWI status.
-      if (message.mwiDiscard) {
-        return true;
-      }
-    }
-
-    let notifyReceived = function notifyReceived(rv, domMessage) {
-      let success = Components.isSuccessCode(rv);
-
-      this.sendAckSms(rv, message);
-
-      if (!success) {
-        // At this point we could send a message to content to notify the user
-        // that storing an incoming SMS failed, most likely due to a full disk.
-        if (DEBUG) {
-          this.debug("Could not store SMS, error code " + rv);
-        }
-        return;
-      }
-
-      this.broadcastSmsSystemMessage(
-        Ci.nsISmsMessenger.NOTIFICATION_TYPE_RECEIVED, domMessage);
-      Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
-    }.bind(this);
-
-    if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
-      gMobileMessageDatabaseService.saveReceivedMessage(message,
-                                                        notifyReceived);
-    } else {
-      message.id = -1;
-      message.threadId = 0;
-      message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
-      message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
-      message.read = false;
-
-      let domMessage =
-        gMobileMessageService.createSmsMessage(message.id,
-                                               message.threadId,
-                                               message.iccId,
-                                               message.delivery,
-                                               message.deliveryStatus,
-                                               message.sender,
-                                               message.receiver,
-                                               message.body,
-                                               message.messageClass,
-                                               message.timestamp,
-                                               message.sentTimestamp,
-                                               0,
-                                               message.read);
-
-      notifyReceived(Cr.NS_OK, domMessage);
-    }
-
-    // SMS ACK will be sent in notifyReceived. Return false here.
-    return false;
-  },
-
-  /**
-   * Handle ACK response of received SMS.
-   */
-  sendAckSms: function(aRv, aMessage) {
-    if (aMessage.messageClass === RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_2]) {
-      return;
-    }
-
-    let result = RIL.PDU_FCS_OK;
-    if (!Components.isSuccessCode(aRv)) {
-      if (DEBUG) this.debug("Failed to handle received sms: " + aRv);
-      result = (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE)
-                ? RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED
-                : RIL.PDU_FCS_UNSPECIFIED;
-    }
-
-    this.workerMessenger.send("ackSMS", { result: result });
-
+  handleSmsReceived: function(aMessage) {
+    let header = aMessage.header;
+    // Concatenation Info:
+    // - segmentRef: a modulo 256 counter indicating the reference number for a
+    //               particular concatenated short message. '0' is a valid number.
+    // - The concatenation info will not be available in |header| if
+    //   segmentSeq or segmentMaxSeq is 0.
+    // See 3GPP TS 23.040, 9.2.3.24.1 Concatenated Short Messages.
+    let segmentRef = (header && header.segmentRef !== undefined)
+      ? header.segmentRef : 1;
+    let segmentSeq = header && header.segmentSeq || 1;
+    let segmentMaxSeq = header && header.segmentMaxSeq || 1;
+    // Application Ports:
+    // The port number ranges from 0 to 49151.
+    // see 3GPP TS 23.040, 9.2.3.24.3/4 Application Port Addressing.
+    let originatorPort = (header && header.originatorPort !== undefined)
+      ? header.originatorPort
+      : Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID;
+    let destinationPort = (header && header.destinationPort !== undefined)
+      ? header.destinationPort
+      : Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID;
+    // MWI info:
+    let mwiPresent = (aMessage.mwi)? true : false;
+    let mwiDiscard = (mwiPresent)? aMessage.mwi.discard: false;
+    let mwiMsgCount = (mwiPresent)? aMessage.mwi.msgCount: 0;
+    let mwiActive = (mwiPresent)? aMessage.mwi.active: false;
+    // CDMA related attributes:
+    let cdmaMessageType = aMessage.messageType || 0;
+    let cdmaTeleservice = aMessage.teleservice || 0;
+    let cdmaServiceCategory = aMessage.serviceCategory || 0;
+
+    gSmsService
+      .notifyMessageReceived(this.clientId,
+                             aMessage.SMSC || null,
+                             aMessage.sentTimestamp,
+                             aMessage.sender,
+                             aMessage.pid,
+                             aMessage.encoding,
+                             RIL.GECKO_SMS_MESSAGE_CLASSES
+                               .indexOf(aMessage.messageClass),
+                             aMessage.language || null,
+                             segmentRef,
+                             segmentSeq,
+                             segmentMaxSeq,
+                             originatorPort,
+                             destinationPort,
+                             mwiPresent,
+                             mwiDiscard,
+                             mwiMsgCount,
+                             mwiActive,
+                             cdmaMessageType,
+                             cdmaTeleservice,
+                             cdmaServiceCategory,
+                             aMessage.body || null,
+                             aMessage.data || [],
+                             (aMessage.data) ? aMessage.data.length : 0);
   },
 
   /**
    * Set the setting value of "time.clock.automatic-update.available".
    */
   setClockAutoUpdateAvailable: function(value) {
     gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null);
   },
@@ -2751,18 +2214,19 @@ RadioInterface.prototype = {
     let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
                   .getService(Ci.nsIGonkVoicemailService);
     service.notifyInfoChanged(this.clientId, message.number, message.alphaId);
   },
 
   handleIccMwis: function(mwi) {
     let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
                   .getService(Ci.nsIGonkVoicemailService);
+    // Note: returnNumber and returnMessage is not available from UICC.
     service.notifyStatusChanged(this.clientId, mwi.active, mwi.msgCount,
-                                mwi.returnNumber, mwi.returnMessage);
+                                null, null);
   },
 
   handleIccInfoChange: function(message) {
     let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
 
     if (!message || !message.iccid) {
       // Card is not detected, clear iccInfo to null.
       this.rilContext.iccInfo = null;