Backout bug 873351, break Mnw test cases.
authorVicamo Yang <vyang@mozilla.com>
Sat, 07 Sep 2013 19:09:54 +0800
changeset 146092 0fadd373bf9985d33b385127f829c7ead8100d88
parent 146091 2a22b79edf4d44cacbff18426ba000aee1d643b6
child 146093 97570ed1ad80224f15f55fdacc947d9535d8a928
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs873351
milestone26.0a1
Backout bug 873351, break Mnw test cases.
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
dom/mobilemessage/interfaces/moz.build
dom/mobilemessage/interfaces/nsIRilSmsService.idl
dom/mobilemessage/interfaces/nsISmsService.idl
dom/mobilemessage/src/Makefile.in
dom/mobilemessage/src/SmsServicesFactory.cpp
dom/mobilemessage/src/android/SmsService.cpp
dom/mobilemessage/src/fallback/SmsService.cpp
dom/mobilemessage/src/gonk/SmsService.cpp
dom/mobilemessage/src/gonk/SmsService.h
dom/mobilemessage/src/gonk/SmsService.js
dom/mobilemessage/src/gonk/SmsService.manifest
dom/mobilemessage/src/ipc/SmsIPCService.cpp
dom/mobilemessage/src/moz.build
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/nsIRadioInterfaceLayer.idl
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -455,18 +455,16 @@
 @BINPATH@/components/SettingsService.manifest
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/NetworkManager.manifest
 @BINPATH@/components/NetworkManager.js
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/MmsService.manifest
 @BINPATH@/components/MmsService.js
-@BINPATH@/components/SmsService.manifest
-@BINPATH@/components/SmsService.js
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/NetworkStatsManager.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -441,18 +441,16 @@
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/MmsService.manifest
 @BINPATH@/components/MmsService.js
-@BINPATH@/components/SmsService.manifest
-@BINPATH@/components/SmsService.js
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 #endif
--- a/dom/mobilemessage/interfaces/moz.build
+++ b/dom/mobilemessage/interfaces/moz.build
@@ -20,13 +20,12 @@ XPIDL_SOURCES += [
     'nsIMobileMessageService.idl',
     'nsISmsService.idl',
     'nsIWapPushApplication.idl',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     XPIDL_SOURCES += [
         'nsIRilMobileMessageDatabaseService.idl',
-        'nsIRilSmsService.idl',
     ]
 
 XPIDL_MODULE = 'dom_mobilemessage'
 
deleted file mode 100644
--- a/dom/mobilemessage/interfaces/nsIRilSmsService.idl
+++ /dev/null
@@ -1,11 +0,0 @@
-/* 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 "nsISmsService.idl"
-
-[scriptable, uuid(f216903c-bdf5-4988-b894-f62fd91df114)]
-interface nsIRilSmsService : nsISmsService
-{
-  void notifyMessageReceived(in jsval message);
-};
--- a/dom/mobilemessage/interfaces/nsISmsService.idl
+++ b/dom/mobilemessage/interfaces/nsISmsService.idl
@@ -8,24 +8,25 @@ interface nsIDOMMozSmsMessage;
 interface nsIDOMMozSmsSegmentInfo;
 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(7ef8e361-9db6-46ed-badc-2901e1049e5d)]
+[scriptable, builtinclass, uuid(0f3f75ec-00dd-11e3-87ac-0b1d5c79afdf)]
 interface nsISmsService : nsISupports
 {
   boolean hasSupport();
 
   void getSegmentInfoForText(in DOMString text,
                              in nsIMobileMessageCallback request);
 
   void send(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);
 };
--- a/dom/mobilemessage/src/Makefile.in
+++ b/dom/mobilemessage/src/Makefile.in
@@ -21,10 +21,16 @@ LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/events/src \
   -I$(topsrcdir)/dom/base \
   $(NULL)
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
 # subdirectory (and the ipc one).
 LOCAL_INCLUDES += $(VPATH:%=-I%)
 
+ifdef MOZ_B2G_RIL
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/system/gonk \
+  $(NULL)
+endif
+
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/dom/mobilemessage/src/SmsServicesFactory.cpp
+++ b/dom/mobilemessage/src/SmsServicesFactory.cpp
@@ -1,60 +1,53 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "SmsServicesFactory.h"
 #include "nsXULAppAPI.h"
+#include "SmsService.h"
 #include "SmsIPCService.h"
 #ifndef MOZ_B2G_RIL
 #include "MobileMessageDatabaseService.h"
 #include "MmsService.h"
-#include "SmsService.h"
 #endif
 #include "nsServiceManagerUtils.h"
 
 #define RIL_MMSSERVICE_CONTRACTID "@mozilla.org/mms/rilmmsservice;1"
-#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID \
-  "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1"
-#define RIL_SMSSERVICE_CONTRACTID "@mozilla.org/sms/rilsmsservice;1"
+#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 /* static */ already_AddRefed<nsISmsService>
 SmsServicesFactory::CreateSmsService()
 {
   nsCOMPtr<nsISmsService> smsService;
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     smsService = new SmsIPCService();
   } else {
-#ifdef MOZ_B2G_RIL
-    smsService = do_GetService(RIL_SMSSERVICE_CONTRACTID);
-#else
     smsService = new SmsService();
-#endif
   }
 
   return smsService.forget();
 }
 
 /* static */ already_AddRefed<nsIMobileMessageDatabaseService>
 SmsServicesFactory::CreateMobileMessageDatabaseService()
 {
   nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService;
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     mobileMessageDBService = new SmsIPCService();
   } else {
 #ifdef MOZ_B2G_RIL
-    mobileMessageDBService =
-      do_GetService(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
+    mobileMessageDBService = do_GetService(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
 #else
     mobileMessageDBService = new MobileMessageDatabaseService();
 #endif
   }
 
   return mobileMessageDBService.forget();
 }
 
--- a/dom/mobilemessage/src/android/SmsService.cpp
+++ b/dom/mobilemessage/src/android/SmsService.cpp
@@ -46,16 +46,24 @@ SmsService::Send(const nsAString& aNumbe
     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/src/fallback/SmsService.cpp
+++ b/dom/mobilemessage/src/fallback/SmsService.cpp
@@ -35,16 +35,24 @@ SmsService::Send(const nsAString& aNumbe
                  const bool       aSilent,
                  nsIMobileMessageCallback* aRequest)
 {
   NS_ERROR("We should not be here!");
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
+SmsService::IsSilentNumber(const nsAString& aNumber,
+                           bool*            aIsSilent)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 SmsService::AddSilentNumber(const nsAString& aNumber)
 {
   NS_ERROR("We should not be here!");
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 SmsService::RemoveSilentNumber(const nsAString& aNumber)
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/src/gonk/SmsService.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "SmsMessage.h"
+#include "SmsService.h"
+#include "jsapi.h"
+#include "SmsSegmentInfo.h"
+
+namespace mozilla {
+namespace dom {
+namespace mobilemessage {
+
+NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
+
+SmsService::SmsService()
+{
+  nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
+  if (ril) {
+    ril->GetRadioInterface(0, getter_AddRefs(mRadioInterface));
+  }
+  NS_WARN_IF_FALSE(mRadioInterface, "This shouldn't fail!");
+}
+
+NS_IMETHODIMP
+SmsService::HasSupport(bool* aHasSupport)
+{
+  *aHasSupport = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsService::GetSegmentInfoForText(const nsAString& aText,
+                                  nsIMobileMessageCallback* aRequest)
+{
+  NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
+
+  return mRadioInterface->GetSegmentInfoForText(aText, aRequest);
+}
+
+NS_IMETHODIMP
+SmsService::Send(const nsAString& aNumber,
+                 const nsAString& aMessage,
+                 const bool       aSilent,
+                 nsIMobileMessageCallback* aRequest)
+{
+  NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
+
+  return mRadioInterface->SendSMS(aNumber, aMessage, aSilent, aRequest);
+}
+
+NS_IMETHODIMP
+SmsService::IsSilentNumber(const nsAString& aNumber,
+                           bool*            aIsSilent)
+{
+  *aIsSilent = mSilentNumbers.Contains(aNumber);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsService::AddSilentNumber(const nsAString& aNumber)
+{
+  if (mSilentNumbers.Contains(aNumber)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_ENSURE_TRUE(mSilentNumbers.AppendElement(aNumber), NS_ERROR_FAILURE);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsService::RemoveSilentNumber(const nsAString& aNumber)
+{
+  if (!mSilentNumbers.Contains(aNumber)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  NS_ENSURE_TRUE(mSilentNumbers.RemoveElement(aNumber), NS_ERROR_FAILURE);
+  return NS_OK;
+}
+
+} // namespace mobilemessage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/src/gonk/SmsService.h
@@ -0,0 +1,35 @@
+/* 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/. */
+
+#ifndef mozilla_dom_mobilemessage_SmsService_h
+#define mozilla_dom_mobilemessage_SmsService_h
+
+#include "nsISmsService.h"
+#include "nsCOMPtr.h"
+#include "nsIRadioInterfaceLayer.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+namespace mobilemessage {
+
+class SmsService : public nsISmsService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISMSSERVICE
+  SmsService();
+
+protected:
+  // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS
+  nsCOMPtr<nsIRadioInterface> mRadioInterface;
+  nsTArray<nsString> mSilentNumbers;
+};
+
+} // namespace mobilemessage
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_mobilemessage_SmsService_h
deleted file mode 100644
--- a/dom/mobilemessage/src/gonk/SmsService.js
+++ /dev/null
@@ -1,1015 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-var RIL = {};
-Cu.import("resource://gre/modules/ril_consts.js", RIL);
-
-const RIL_SMSSERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsservice;1";
-const RIL_SMSSERVICE_CID =
-  Components.ID("{46a9ed78-3574-40a1-9f12-ea179942d67f}");
-
-const DELIVERY_STATE_RECEIVED = "received";
-const DELIVERY_STATE_SENDING  = "sending";
-const DELIVERY_STATE_SENT     = "sent";
-const DELIVERY_STATE_ERROR    = "error";
-
-// Observer topics to send.
-const kSmsReceivedObserverTopic          = "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 kSilentSmsReceivedObserverTopic    = "silent-sms-received";
-
-// Observer topics to watch.
-const kPrefenceChangedObserverTopic = "nsPref:changed";
-const kXpcomShutdownObserverTopic   = "xpcom-shutdown";
-
-// Preference keys.
-const kPrefKeyRilDebuggingEnabled = "ril.debugging.enabled";
-
-// TODO: Bug 815526 - [meta] B2G RIL: deprecate RILContentHelper
-XPCOMUtils.defineLazyServiceGetter(this, "gIccProvider",
-                                   "@mozilla.org/ril/content-helper;1",
-                                   "nsIIccProvider");
-
-XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
-                                   "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
-                                   "nsIRilMobileMessageDatabaseService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
-                                   "@mozilla.org/mobilemessage/mobilemessageservice;1",
-                                   "nsIMobileMessageService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
-                                   "@mozilla.org/system-message-internal;1",
-                                   "nsISystemMessagesInternal");
-
-XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () {
-  let ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
-  // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS
-  return ril.getRadioInterface(0);
-});
-
-XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function () {
-  let ns = {};
-  Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
-  return ns.PhoneNumberUtils;
-});
-
-XPCOMUtils.defineLazyGetter(this, "WAP", function () {
-  let WAP = {};
-  Cu.import("resource://gre/modules/WapPushManager.js", WAP);
-  return WAP;
-});
-
-// TODO: Bug 833229 - B2G RIL: use ipdl as IPC in MozVoicemail
-XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
-  let ns = {};
-  Cu.import("resource://gre/modules/RilMessageManager.jsm", ns);
-  return ns.RilMessageManager;
-});
-
-let DEBUG;
-function debug(s) {
-  dump("SmsService: " + s + "\n");
-}
-
-/**
- * SmsService
- */
-function SmsService() {
-  this._updateDebugFlag();
-
-  this.silentNumbers = [];
-
-  this.portAddressedSmsApps = {};
-  this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] =
-    this._handleSmsWdpPortPush.bind(this);
-
-  Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false);
-  Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
-}
-SmsService.prototype = {
-
-  classID: RIL_SMSSERVICE_CID,
-  classInfo: XPCOMUtils.generateCI({classID: RIL_SMSSERVICE_CID,
-                                    contractID: RIL_SMSSERVICE_CONTRACTID,
-                                    classDescription: "SmsService",
-                                    interfaces: [Ci.nsIRilSmsService,
-                                                 Ci.nsISmsService],
-                                    flags: Ci.nsIClassInfo.SINGLETON}),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIRilSmsService,
-                                         Ci.nsISmsService]),
-
-  /**
-   * List of tuples of national language identifier pairs.
-   *
-   * TODO: Support static/runtime settings, see bug 733331.
-   */
-  enabledGsmTableTuples: [
-    [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
-  ],
-
-  /**
-   * Use 16-bit reference number for concatenated outgoint messages.
-   *
-   * TODO: Support static/runtime settings, see bug 733331.
-   */
-  segmentRef16Bit: false,
-
-  /**
-   * Get valid SMS concatenation reference number.
-   */
-  segmentRef: 0,
-  get nextSegmentRef() {
-    let ref = this.segmentRef++;
-
-    this.segmentRef %= (this.segmentRef16Bit ? 65535 : 255);
-
-    // 0 is not a valid SMS concatenation reference number.
-    return ref + 1;
-  },
-
-  statusReportPendingMessageIds: null,
-
-  portAddressedSmsApps: null,
-
-  silentNumbers: null,
-
-  _updateDebugFlag: function _updateDebugFlag() {
-    try {
-      DEBUG = RIL.DEBUG_RIL ||
-              Services.prefs.getBoolPref("ril.debugging.enabled");
-    } catch (e) {}
-  },
-
-  _getStrict7BitEncoding: function _getStrict7BitEncoding() {
-    try {
-      return Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
-    } catch (e) {
-      return false;
-    }
-  },
-
-  _getRequestStatusReport: function _getRequestStatusReport() {
-    try {
-      return Services.prefs.getBoolPref("dom.sms.requestStatusReport");
-    } catch (e) {
-      return true;
-    }
-  },
-
-  _getMsisdn: function _getMsisdn() {
-    let iccInfo = gIccProvider.iccInfo;
-    if (!iccInfo) {
-      return null;
-    }
-
-    let number;
-    if (iccInfo.iccType == "ruim") {
-      let cdmaIccInfo = iccInfo.QueryInterface(Ci.nsIDOMMozCdmaIccInfo);
-      number = cdmaIccInfo.mdn;
-    } else {
-      let gsmIccInfo = iccInfo.QueryInterface(Ci.nsIDOMMozGsmIccInfo);
-      number = gsmIccInfo.msisdn;
-    }
-
-    // Workaround an xpconnect issue with undefined string objects.
-    // See bug 808220
-    if (number === undefined || number === "undefined") {
-      return null;
-    }
-    return number;
-  },
-
-  /**
-   * Calculate encoded length using specified locking/single shift table
-   *
-   * @param text
-   *        text string to be encoded.
-   * @param langTable
-   *        locking shift table string.
-   * @param langShiftTable
-   *        single shift table string.
-   * @param strict7BitEncoding
-   *        Optional. Enable Latin characters replacement with corresponding
-   *        ones in GSM SMS 7-bit default alphabet.
-   *
-   * @return encoded length in septets.
-   *
-   * @note that the algorithm used in this function must match exactly with
-   * GsmPDUHelper#writeStringAsSeptets.
-   */
-  _countGsm7BitSeptets: function _countGsm7BitSeptets(text,
-                                                      langTable,
-                                                      langShiftTable,
-                                                      strict7BitEncoding) {
-    let length = 0;
-    for (let msgIndex = 0; msgIndex < text.length; msgIndex++) {
-      let c = text.charAt(msgIndex);
-      if (strict7BitEncoding) {
-        c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
-      }
-
-      let septet = langTable.indexOf(c);
-
-      // According to 3GPP TS 23.038, section 6.1.1 General notes, "The
-      // characters marked '1)' are not used but are displayed as a space."
-      if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
-        continue;
-      }
-
-      if (septet >= 0) {
-        length++;
-        continue;
-      }
-
-      septet = langShiftTable.indexOf(c);
-      if (septet < 0) {
-        if (!strict7BitEncoding) {
-          return -1;
-        }
-
-        // Bug 816082, when strict7BitEncoding is enabled, we should replace
-        // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
-        c = '*';
-        if (langTable.indexOf(c) >= 0) {
-          length++;
-        } else if (langShiftTable.indexOf(c) >= 0) {
-          length += 2;
-        } else {
-          // We can't even encode a '*' character with current configuration.
-          return -1;
-        }
-
-        continue;
-      }
-
-      // According to 3GPP TS 23.038 B.2, "This code represents a control
-      // character and therefore must not be used for language specific
-      // characters."
-      if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
-        continue;
-      }
-
-      // The character is not found in locking shfit table, but could be
-      // encoded as <escape><char> with single shift table. Note that it's
-      // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
-      // but we can display it as a space in this case as said in previous
-      // comment.
-      length += 2;
-    }
-
-    return length;
-  },
-
-  /**
-   * Calculate user data length of specified text string encoded in GSM 7Bit
-   * alphabets.
-   *
-   * @param text
-   *        a text string to be encoded.
-   * @param strict7BitEncoding
-   *        Optional. Enable Latin characters replacement with corresponding
-   *        ones in GSM SMS 7-bit default alphabet.
-   *
-   * @return null or an options object with attributes `dcs`,
-   *         `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`,
-   *         `langShiftIndex`, `segmentMaxSeq` set.
-   *
-   * @see #_calculateUserDataLength().
-   */
-  _calculateUserDataLength7Bit: function _calculateUserDataLength7Bit(text,
-                                                                      strict7BitEncoding) {
-    let options = null;
-    let minUserDataSeptets = Number.MAX_VALUE;
-    for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
-      let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
-
-      const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
-      const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
-
-      let bodySeptets = this._countGsm7BitSeptets(text,
-                                                  langTable,
-                                                  langShiftTable,
-                                                  strict7BitEncoding);
-      if (bodySeptets < 0) {
-        continue;
-      }
-
-      let headerLen = 0;
-      if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
-        headerLen += 3; // IEI + len + langIndex
-      }
-      if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
-        headerLen += 3; // IEI + len + langShiftIndex
-      }
-
-      // Calculate full user data length, note the extra byte is for header len
-      let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
-      let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT;
-      if ((bodySeptets + headerSeptets) > segmentSeptets) {
-        headerLen += this.segmentRef16Bit ? 6 : 5;
-        headerSeptets = Math.ceil((headerLen + 1) * 8 / 7);
-        segmentSeptets -= headerSeptets;
-      }
-
-      let segments = Math.ceil(bodySeptets / segmentSeptets);
-      let userDataSeptets = bodySeptets + headerSeptets * segments;
-      if (userDataSeptets >= minUserDataSeptets) {
-        continue;
-      }
-
-      minUserDataSeptets = userDataSeptets;
-
-      options = {
-        dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET,
-        encodedFullBodyLength: bodySeptets,
-        userDataHeaderLength: headerLen,
-        langIndex: langIndex,
-        langShiftIndex: langShiftIndex,
-        segmentMaxSeq: segments,
-        segmentChars: segmentSeptets,
-      };
-    }
-
-    return options;
-  },
-
-  /**
-   * Calculate user data length of specified text string encoded in UCS2.
-   *
-   * @param text
-   *        a text string to be encoded.
-   *
-   * @return an options object with attributes `dcs`, `userDataHeaderLength`,
-   *         `encodedFullBodyLength`, `segmentMaxSeq` set.
-   *
-   * @see #_calculateUserDataLength().
-   */
-  _calculateUserDataLengthUCS2: function _calculateUserDataLengthUCS2(text) {
-    let bodyChars = text.length;
-    let headerLen = 0;
-    let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2);
-    let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2;
-    if ((bodyChars + headerChars) > segmentChars) {
-      headerLen += this.segmentRef16Bit ? 6 : 5;
-      headerChars = Math.ceil((headerLen + 1) / 2);
-      segmentChars -= headerChars;
-    }
-
-    let segments = Math.ceil(bodyChars / segmentChars);
-
-    return {
-      dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET,
-      encodedFullBodyLength: bodyChars * 2,
-      userDataHeaderLength: headerLen,
-      segmentMaxSeq: segments,
-      segmentChars: segmentChars,
-    };
-  },
-
-  /**
-   * Calculate user data length and its encoding.
-   *
-   * @param text
-   *        a text string to be encoded.
-   * @param strict7BitEncoding
-   *        Optional. Enable Latin characters replacement with corresponding
-   *        ones in GSM SMS 7-bit default alphabet.
-   *
-   * @return an options object with some or all of following attributes set:
-   *
-   * @param dcs
-   *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
-   *        constants.
-   * @param userDataHeaderLength
-   *        Length of embedded user data header, in bytes. The whole header
-   *        size will be userDataHeaderLength + 1; 0 for no header.
-   * @param encodedFullBodyLength
-   *        Length of the text body 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 segmentMaxSeq
-   *        Max sequence number of a multi-part messages, or 1 for single one.
-   *        This number might not be accurate for a multi-part text until
-   *        it's processed by #_fragmentText() again.
-   */
-  _calculateUserDataLength: function _calculateUserDataLength(text,
-                                                              strict7BitEncoding) {
-    let options = this._calculateUserDataLength7Bit(text, strict7BitEncoding);
-    if (!options) {
-      options = this._calculateUserDataLengthUCS2(text);
-    }
-
-    if (DEBUG) debug("_calculateUserDataLength: " + JSON.stringify(options));
-    return options;
-  },
-
-  /**
-   * Fragment GSM 7-Bit encodable string for transmission.
-   *
-   * @param text
-   *        text string to be fragmented.
-   * @param langTable
-   *        locking shift table string.
-   * @param langShiftTable
-   *        single shift table string.
-   * @param segmentSeptets
-   *        Number of available spetets per segment.
-   * @param strict7BitEncoding
-   *        Optional. Enable Latin characters replacement with corresponding
-   *        ones in GSM SMS 7-bit default alphabet.
-   *
-   * @return an array of objects. See #_fragmentText() for detailed definition.
-   */
-  _fragmentText7Bit: function _fragmentText7Bit(text, langTable, langShiftTable,
-                                                segmentSeptets,
-                                                strict7BitEncoding) {
-    let ret = [];
-    let body = "", len = 0;
-    for (let i = 0, inc = 0; i < text.length; i++) {
-      let c = text.charAt(i);
-      if (strict7BitEncoding) {
-        c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
-      }
-
-      let septet = langTable.indexOf(c);
-      if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
-        continue;
-      }
-
-      if (septet >= 0) {
-        inc = 1;
-      } else {
-        septet = langShiftTable.indexOf(c);
-        if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
-          continue;
-        }
-
-        inc = 2;
-        if (septet < 0) {
-          if (!strict7BitEncoding) {
-            throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!");
-          }
-
-          // Bug 816082, when strict7BitEncoding is enabled, we should replace
-          // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
-          c = '*';
-          if (langTable.indexOf(c) >= 0) {
-            inc = 1;
-          }
-        }
-      }
-
-      if ((len + inc) > segmentSeptets) {
-        ret.push({
-          body: body,
-          encodedBodyLength: len,
-        });
-        body = c;
-        len = inc;
-      } else {
-        body += c;
-        len += inc;
-      }
-    }
-
-    if (len) {
-      ret.push({
-        body: body,
-        encodedBodyLength: len,
-      });
-    }
-
-    return ret;
-  },
-
-  /**
-   * Fragment UCS2 encodable string for transmission.
-   *
-   * @param text
-   *        text string to be fragmented.
-   * @param segmentChars
-   *        Number of available characters per segment.
-   *
-   * @return an array of objects. See #_fragmentText() for detailed definition.
-   */
-  _fragmentTextUCS2: function _fragmentTextUCS2(text, segmentChars) {
-    let ret = [];
-    for (let offset = 0; offset < text.length; offset += segmentChars) {
-      let str = text.substr(offset, segmentChars);
-      ret.push({
-        body: str,
-        encodedBodyLength: str.length * 2,
-      });
-    }
-
-    return ret;
-  },
-
-  /**
-   * Fragment string for transmission.
-   *
-   * Fragment input text string into an array of objects that contains
-   * attributes `body`, substring for this segment, `encodedBodyLength`,
-   * length of the encoded segment body in septets.
-   *
-   * @param text
-   *        Text string to be fragmented.
-   * @param options
-   *        Optional pre-calculated option object. The output array will be
-   *        stored at options.segments if there are multiple segments.
-   * @param strict7BitEncoding
-   *        Optional. Enable Latin characters replacement with corresponding
-   *        ones in GSM SMS 7-bit default alphabet.
-   *
-   * @return Populated options object.
-   */
-  _fragmentText: function _fragmentText(text, options, strict7BitEncoding) {
-    if (!options) {
-      options = this._calculateUserDataLength(text, strict7BitEncoding);
-    }
-
-    if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
-      const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex];
-      const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex];
-      options.segments = this._fragmentText7Bit(text,
-                                                langTable, langShiftTable,
-                                                options.segmentChars,
-                                                strict7BitEncoding);
-    } else {
-      options.segments = this._fragmentTextUCS2(text,
-                                                options.segmentChars);
-    }
-
-    // Re-sync options.segmentMaxSeq with actual length of returning array.
-    options.segmentMaxSeq = options.segments.length;
-
-    return options;
-  },
-
-  /**
-   * 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.
-   */
-  _broadcastSystemMessage: function _broadcastSystemMessage(aName, aDomMessage) {
-    if (DEBUG) debug("Broadcasting the SMS system message: " + aName);
-
-    // 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.
-    gSystemMessenger.broadcastMessage(aName, {
-      type:           aDomMessage.type,
-      id:             aDomMessage.id,
-      threadId:       aDomMessage.threadId,
-      delivery:       aDomMessage.delivery,
-      deliveryStatus: aDomMessage.deliveryStatus,
-      sender:         aDomMessage.sender,
-      receiver:       aDomMessage.receiver,
-      body:           aDomMessage.body,
-      messageClass:   aDomMessage.messageClass,
-      timestamp:      aDomMessage.timestamp,
-      read:           aDomMessage.read
-    });
-  },
-
-  /**
-   * Handle WDP port push PDU. Constructor WDP bearer information and deliver
-   * to WapPushManager.
-   *
-   * @param message
-   *        A SMS message.
-   */
-  _handleSmsWdpPortPush: function _handleSmsWdpPortPush(message) {
-    if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      if (DEBUG) {
-        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.header.originatorPort,
-      destinationAddress: this.rilContext.iccInfo.msisdn,
-      destinationPort: message.header.destinationPort,
-    };
-    WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
-                                     0, options);
-  },
-
-  _isSilentNumber: function _isSilentNumber(number) {
-    return this.silentNumbers.indexOf(number) >= 0;
-  },
-
-  /**
-   * nsISmsService methods.
-   */
-
-  // TODO: Bug 859616 - WebSMS: return undefined if the API is unsupported on
-  // the platform, not null
-  hasSupport: function hasSupport() {
-    return true;
-  },
-
-  getSegmentInfoForText: function getSegmentInfoForText(text, request) {
-    let strict7BitEncoding = this._getStrict7BitEncoding();
-
-    let options = this._fragmentText(text, null, strict7BitEncoding);
-    let charsInLastSegment;
-    if (options.segmentMaxSeq) {
-      let lastSegment = options.segments[options.segmentMaxSeq - 1];
-      charsInLastSegment = lastSegment.encodedBodyLength;
-      if (options.dcs == RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET) {
-        // In UCS2 encoding, encodedBodyLength is in octets.
-        charsInLastSegment /= 2;
-      }
-    } else {
-      charsInLastSegment = 0;
-    }
-
-    let result = gMobileMessageService
-                 .createSmsSegmentInfo(options.segmentMaxSeq,
-                                       options.segmentChars,
-                                       options.segmentChars - charsInLastSegment);
-    request.notifySegmentInfoForTextGot(result);
-  },
-
-  send: function send(number, text, silent, request) {
-    let strict7BitEncoding = this._getStrict7BitEncoding();
-    let requestStatusReport = this._getRequestStatusReport();
-
-    let options = this._fragmentText(text, null, strict7BitEncoding);
-    options.number = gPhoneNumberUtils.normalize(number);
-    options.requestStatusReport = requestStatusReport && !silent;
-    if (options.segmentMaxSeq > 1) {
-      options.segmentRef16Bit = this.segmentRef16Bit;
-      options.segmentRef = this.nextSegmentRef;
-    }
-
-    let notifyResult = (function notifyResult(rv, domMessage) {
-      // TODO bug 832140 handle !Components.isSuccessCode(rv)
-      if (!silent) {
-        Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
-      }
-
-      // If the radio is disabled or the SIM card is not ready, just directly
-      // return with the corresponding error code.
-      let errorCode;
-      if (!gPhoneNumberUtils.isPlainPhoneNumber(options.number)) {
-        if (DEBUG) debug("Error! Address is invalid when sending SMS: " +
-                              options.number);
-        errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
-      } else if (gRadioInterface.rilContext.radioState !=
-                 RIL.GECKO_RADIOSTATE_READY) {
-        if (DEBUG) debug("Error! Radio is disabled when sending SMS.");
-        errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
-      } else if (gRadioInterface.rilContext.cardState !=
-                 RIL.GECKO_CARDSTATE_READY) {
-        if (DEBUG) debug("Error! SIM card is not ready when sending SMS.");
-        errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
-      }
-      if (errorCode) {
-        if (silent) {
-          request.notifySendMessageFailed(errorCode);
-          return;
-        }
-
-        gMobileMessageDatabaseService
-          .setMessageDeliveryByMessageId(domMessage.id,
-                                         null,
-                                         DELIVERY_STATE_ERROR,
-                                         RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
-                                         null,
-                                         function notifyResult(rv, domMessage) {
-          // TODO bug 832140 handle !Components.isSuccessCode(rv)
-          request.notifySendMessageFailed(errorCode);
-          Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
-        });
-        return;
-      }
-
-      // Keep current SMS message info for sent/delivered notifications
-      let context = {
-        request: request,
-        sms: domMessage,
-        requestStatusReport: options.requestStatusReport,
-        silent: silent
-      };
-
-      // This is the entry point starting to send SMS.
-      gRadioInterface.sendWorkerMessage("sendSMS", options,
-                                        (function(context, response) {
-        if (response.errorMsg) {
-          // Failed to send SMS out.
-          let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR;
-          switch (response.errorMsg) {
-            case RIL.ERROR_RADIO_NOT_AVAILABLE:
-              error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
-              break;
-          }
-
-          if (context.silent) {
-            context.request.notifySendMessageFailed(error);
-            return false;
-          }
-
-          gMobileMessageDatabaseService
-            .setMessageDeliveryByMessageId(context.sms.id,
-                                           null,
-                                           DELIVERY_STATE_ERROR,
-                                           RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
-                                           null,
-                                           function notifyResult(rv, domMessage) {
-            // TODO bug 832140 handle !Components.isSuccessCode(rv)
-            context.request.notifySendMessageFailed(error);
-            Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
-          });
-          return false;
-        } // End of send failure.
-
-        if (response.deliveryStatus) {
-          // Message delivery.
-          gMobileMessageDatabaseService
-            .setMessageDeliveryByMessageId(context.sms.id,
-                                           null,
-                                           context.sms.delivery,
-                                           response.deliveryStatus,
-                                           null,
-                                           function notifyResult(rv, domMessage) {
-            // TODO bug 832140 handle !Components.isSuccessCode(rv)
-            let topic = (response.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
-                        ? kSmsDeliverySuccessObserverTopic
-                        : kSmsDeliveryErrorObserverTopic;
-            Services.obs.notifyObservers(domMessage, topic, null);
-          });
-
-          // Send transaction has ended completely.
-          return false;
-        } // End of message delivery.
-
-        // Message sent.
-        if (context.silent) {
-          // There is no way to modify nsIDOMMozSmsMessage attributes as they are
-          // read only so we just create a new sms instance to send along with
-          // the notification.
-          let sms = context.sms;
-          context.request.notifyMessageSent(
-            gMobileMessageService.createSmsMessage(sms.id,
-                                                   sms.threadId,
-                                                   DELIVERY_STATE_SENT,
-                                                   sms.deliveryStatus,
-                                                   sms.sender,
-                                                   sms.receiver,
-                                                   sms.body,
-                                                   sms.messageClass,
-                                                   sms.timestamp,
-                                                   sms.read));
-          // We don't wait for SMS-DELIVER-REPORT for silent one.
-          return false;
-        }
-
-        gMobileMessageDatabaseService
-          .setMessageDeliveryByMessageId(context.sms.id,
-                                         null,
-                                         DELIVERY_STATE_SENT,
-                                         context.sms.deliveryStatus,
-                                         null,
-                                         (function notifyResult(rv, domMessage) {
-          // TODO bug 832140 handle !Components.isSuccessCode(rv)
-          this._broadcastSystemMessage("sms-sent", domMessage);
-
-          if (context.requestStatusReport) {
-            context.sms = domMessage;
-          }
-
-          context.request.notifyMessageSent(domMessage);
-          Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null);
-        }).bind(this));
-
-        // Only keep current context if we have requested for delivery report.
-        return context.requestStatusReport;
-      }).bind(this, context)); // End of |sendWorkerMessage| callback.
-    }).bind(this); // End of DB saveSendingMessage callback.
-
-    let sendingMessage = {
-      type: "sms",
-      sender: this._getMsisdn(),
-      receiver: number,
-      body: text,
-      deliveryStatusRequested: options.requestStatusReport,
-      timestamp: Date.now()
-    };
-
-    if (silent) {
-      let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING;
-      let delivery = DELIVERY_STATE_SENDING;
-      let domMessage =
-        gMobileMessageService.createSmsMessage(-1, // id
-                                               0,  // threadId
-                                               delivery,
-                                               deliveryStatus,
-                                               sendingMessage.sender,
-                                               sendingMessage.receiver,
-                                               sendingMessage.body,
-                                               "normal", // message class
-                                               sendingMessage.timestamp,
-                                               false);
-      notifyResult(Cr.NS_OK, domMessage);
-      return;
-    }
-
-    let id = gMobileMessageDatabaseService.saveSendingMessage(
-      sendingMessage, notifyResult);
-  },
-
-  addSilentNumber: function addSilentNumber(number) {
-    if (this._isSilentNumber(number)) {
-      throw Cr.NS_ERROR_UNEXPECTED;
-    }
-
-    this.silentNumbers.push(number);
-  },
-
-  removeSilentNumber: function removeSilentNumber(number) {
-    let index = this.silentNumbers.indexOf(number);
-    if (index < 0) {
-      throw Cr.NS_ERROR_INVALID_ARG;
-    }
-
-    this.silentNumbers.splice(index, 1);
-  },
-
-  /**
-   * nsIRilSmsService methods.
-   */
-
-  notifyMessageReceived: function notifyMessageReceived(message) {
-    if (DEBUG) debug("notifyMessageReceived: " + JSON.stringify(message));
-
-    // FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers
-    if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      message.fullData = new Uint8Array(message.fullData);
-    }
-
-    // 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.header && message.header.destinationPort != null) {
-      let handler = this.portAddressedSmsApps[message.header.destinationPort];
-      if (handler) {
-        handler(message);
-      }
-      gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK });
-      return;
-    }
-
-    if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
-      // Don't know how to handle binary data yet.
-      gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK });
-      return;
-    }
-
-    message.type = "sms";
-    message.sender = message.sender || null;
-    message.receiver = this._getMsisdn();
-    message.body = message.fullBody = message.fullBody || null;
-    message.timestamp = Date.now();
-
-    if (this._isSilentNumber(message.sender)) {
-      message.id = -1;
-      message.threadId = 0;
-      message.delivery = DELIVERY_STATE_RECEIVED;
-      message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
-      message.read = false;
-
-      let domMessage =
-        gMobileMessageService.createSmsMessage(message.id,
-                                               message.threadId,
-                                               message.delivery,
-                                               message.deliveryStatus,
-                                               message.sender,
-                                               message.receiver,
-                                               message.body,
-                                               message.messageClass,
-                                               message.timestamp,
-                                               message.read);
-
-      Services.obs.notifyObservers(domMessage,
-                                   kSilentSmsReceivedObserverTopic,
-                                   null);
-      gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK });
-      return;
-    }
-
-    // TODO: Bug #768441
-    // For now we don't store indicators persistently. When the mwi.discard
-    // flag is false, we'll need to persist the indicator to EFmwis.
-    // See TS 23.040 9.2.3.24.2
-
-    let mwi = message.mwi;
-    if (mwi) {
-      mwi.returnNumber = message.sender;
-      mwi.returnMessage = message.fullBody;
-      // TODO: Bug 833229 - B2G RIL: use ipdl as IPC in MozVoicemail
-      gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
-                                           this.clientId, mwi);
-      gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK });
-      return;
-    }
-
-    let notifyReceived = function notifyReceived(rv, domMessage) {
-      let success = Components.isSuccessCode(rv);
-
-      // Acknowledge the reception of the SMS.
-      gRadioInterface.sendWorkerMessage("ackSMS", {
-        result: (success ? RIL.PDU_FCS_OK
-                         : RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
-      });
-
-      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 " + message.id + ", error code " + rv);
-        }
-        return;
-      }
-
-      this._broadcastSystemMessage("sms-received", domMessage);
-      Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
-    }.bind(this);
-
-    if (message.messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
-      message.id = -1;
-      message.threadId = 0;
-      message.delivery = DELIVERY_STATE_RECEIVED;
-      message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
-      message.read = false;
-
-      let domMessage =
-        gMobileMessageService.createSmsMessage(message.id,
-                                               message.threadId,
-                                               message.delivery,
-                                               message.deliveryStatus,
-                                               message.sender,
-                                               message.receiver,
-                                               message.body,
-                                               message.messageClass,
-                                               message.timestamp,
-                                               message.read);
-
-      notifyReceived(Cr.NS_OK, domMessage);
-      return;
-    }
-
-    message.id =
-      gMobileMessageDatabaseService.saveReceivedMessage(message,
-                                                        notifyReceived);
-  },
-
-  /**
-   * nsIObserver methods.
-   */
-
-  observe: function observe(subject, topic, data) {
-    switch (topic) {
-      case kPrefenceChangedObserverTopic:
-        if (data === "ril.debugging.enabled") {
-          this._updateDebugFlag();
-        }
-        break;
-
-      case kXpcomShutdownObserverTopic:
-        Services.obs.removeObserver(this, kPrefenceChangedObserverTopic);
-        Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
-        break;
-    }
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsService]);
deleted file mode 100644
--- a/dom/mobilemessage/src/gonk/SmsService.manifest
+++ /dev/null
@@ -1,3 +0,0 @@
-# SmsService.js
-component {46a9ed78-3574-40a1-9f12-ea179942d67f} SmsService.js
-contract @mozilla.org/sms/rilsmsservice;1 {46a9ed78-3574-40a1-9f12-ea179942d67f}
--- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
@@ -111,16 +111,24 @@ SmsIPCService::Send(const nsAString& aNu
 {
   return SendRequest(SendMessageRequest(SendSmsMessageRequest(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/mobilemessage/src/moz.build
+++ b/dom/mobilemessage/src/moz.build
@@ -31,17 +31,16 @@ if CONFIG['MOZ_B2G_RIL']:
 else:
     EXPORTS.mozilla.dom.mobilemessage += [
         "%s/%s" % (mmsdir, 'MmsService.h'),
         "%s/%s" % (mmsdir, 'MobileMessageDatabaseService.h'),
     ]
     CPP_SOURCES += [
         'MobileMessageDatabaseService.cpp',
         'MmsService.cpp',
-        'SmsService.cpp',
     ]
 
 EXPORTS.mozilla.dom += [
     'MmsMessage.h',
     'MobileMessageManager.h',
     'SmsFilter.h',
     'SmsMessage.h',
     'SmsSegmentInfo.h',
@@ -56,27 +55,26 @@ CPP_SOURCES += [
     'MobileMessageService.cpp',
     'MobileMessageThread.cpp',
     'SmsChild.cpp',
     'SmsFilter.cpp',
     'SmsIPCService.cpp',
     'SmsMessage.cpp',
     'SmsParent.cpp',
     'SmsSegmentInfo.cpp',
+    'SmsService.cpp',
     'SmsServicesFactory.cpp',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     EXTRA_COMPONENTS += [
         'gonk/MmsService.js',
         'gonk/MmsService.manifest',
         'gonk/MobileMessageDatabaseService.js',
         'gonk/MobileMessageDatabaseService.manifest',
-        'gonk/SmsService.js',
-        'gonk/SmsService.manifest',
     ]
 
 IPDL_SOURCES += [
     'ipc/PMobileMessageCursor.ipdl',
     'ipc/PSms.ipdl',
     'ipc/PSmsRequest.ipdl',
     'ipc/SmsTypes.ipdlh',
 ]
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -42,27 +42,39 @@ function debug(s) {
 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 kNetworkInterfaceStateChangedTopic = "network-interface-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 kTimeNitzAutomaticUpdateEnabled    = "time.nitz.automatic-update.enabled";
 const kTimeNitzAvailable                 = "time.nitz.available";
 const kCellBroadcastSearchList           = "ril.cellbroadcast.searchlist";
 const kCellBroadcastDisabled             = "ril.cellbroadcast.disabled";
 const kPrefenceChangedObserverTopic      = "nsPref:changed";
 const kClirModePreference                = "ril.clirMode";
 
+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 RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:SendMMI",
@@ -98,19 +110,31 @@ const RIL_IPC_ICCMANAGER_MSG_NAMES = [
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact"
 ];
 
 const RIL_IPC_VOICEMAIL_MSG_NAMES = [
   "RIL:GetVoicemailInfo"
 ];
 
+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/rilsmsservice;1",
-                                   "nsIRilSmsService");
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
+                                   "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
+                                   "nsIRilMobileMessageDatabaseService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
@@ -126,16 +150,28 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager",
                                    "@mozilla.org/telephony/system-worker-manager;1",
                                    "nsISystemWorkerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyProvider",
                                    "@mozilla.org/telephony/telephonyprovider;1",
                                    "nsIGonkTelephonyProvider");
 
+XPCOMUtils.defineLazyGetter(this, "WAP", function () {
+  let wap = {};
+  Cu.import("resource://gre/modules/WapPushManager.js", wap);
+  return wap;
+});
+
+XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function () {
+  let ns = {};
+  Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
+  return ns.PhoneNumberUtils;
+});
+
 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
   let ns = {};
   Cu.import("resource://gre/modules/RilMessageManager.jsm", ns);
   return ns.RilMessageManager;
 });
 
 function RadioInterfaceLayer() {
   let callback = this._receiveMessage.bind(this);
@@ -426,16 +462,19 @@ function RadioInterface(options) {
 
   Services.obs.addObserver(this, "xpcom-shutdown", false);
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
   Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
   Services.obs.addObserver(this, kScreenStateChangedTopic, false);
 
   Services.prefs.addObserver(kCellBroadcastDisabled, this, false);
+
+  this.portAddressedSmsApps = {};
+  this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
 }
 RadioInterface.prototype = {
 
   classID:   RADIOINTERFACE_CID,
   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACE_CID,
                                     classDescription: "RadioInterface",
                                     interfaces: [Ci.nsIRadioInterface]}),
 
@@ -653,18 +692,21 @@ RadioInterface.prototype = {
         this.handleRadioStateChange(message);
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
-        gSmsService.notifyMessageReceived(message);
-        break;
+        let ackOk = this.handleSmsReceived(message);
+        if (ackOk) {
+          this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK });
+        }
+        return;
       case "cellbroadcast-received":
         message.timestamp = Date.now();
         gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
                                                  this.clientId, message);
         break;
       case "datacallstatechange":
         this.handleDataCallState(message);
         break;
@@ -701,16 +743,28 @@ RadioInterface.prototype = {
         this.handleExitEmergencyCbMode(message);
         break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
+  getMsisdn: function getMsisdn() {
+    let iccInfo = this.rilContext.iccInfo;
+    let number = iccInfo ? iccInfo.msisdn : null;
+
+    // Workaround an xpconnect issue with undefined string objects.
+    // See bug 808220
+    if (number === undefined || number === "undefined") {
+      return null;
+    }
+    return number;
+  },
+
   updateNetworkInfo: function updateNetworkInfo(message) {
     let voiceMessage = message[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
     let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
     let operatorMessage = message[RIL.NETWORK_INFO_OPERATOR];
     let selectionMessage = message[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
     let signalMessage = message[RIL.NETWORK_INFO_SIGNAL];
 
     // Batch the *InfoChanged messages together
@@ -1268,16 +1322,194 @@ RadioInterface.prototype = {
    */
   handleEmergencyCbModeChange: function handleEmergencyCbModeChange(message) {
     if (DEBUG) this.debug("handleEmergencyCbModeChange: " + JSON.stringify(message));
     gMessageManager.sendMobileConnectionMessage("RIL:EmergencyCbModeChanged",
                                                 this.clientId, message);
   },
 
   /**
+   * Handle WDP port push PDU. Constructor WDP bearer information and deliver
+   * to WapPushManager.
+   *
+   * @param message
+   *        A SMS message.
+   */
+  handleSmsWdpPortPush: function handleSmsWdpPortPush(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.header.originatorPort,
+      destinationAddress: this.rilContext.iccInfo.msisdn,
+      destinationPort: message.header.destinationPort,
+    };
+    WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
+                                     0, options);
+  },
+
+  /**
+   * 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 broadcastSmsSystemMessage(aName, aDomMessage) {
+    if (DEBUG) this.debug("Broadcasting the SMS system message: " + aName);
+
+    // 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.
+    gSystemMessenger.broadcastMessage(aName, {
+      type:           aDomMessage.type,
+      id:             aDomMessage.id,
+      threadId:       aDomMessage.threadId,
+      delivery:       aDomMessage.delivery,
+      deliveryStatus: aDomMessage.deliveryStatus,
+      sender:         aDomMessage.sender,
+      receiver:       aDomMessage.receiver,
+      body:           aDomMessage.body,
+      messageClass:   aDomMessage.messageClass,
+      timestamp:      aDomMessage.timestamp,
+      read:           aDomMessage.read
+    });
+  },
+
+  portAddressedSmsApps: null,
+  handleSmsReceived: function handleSmsReceived(message) {
+    if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
+
+    // FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers
+    if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
+      message.fullData = new Uint8Array(message.fullData);
+    }
+
+    // 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.header && message.header.destinationPort != null) {
+      let handler = this.portAddressedSmsApps[message.header.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.getMsisdn();
+    message.body = message.fullBody = message.fullBody || null;
+    message.timestamp = Date.now();
+
+    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.delivery,
+                                               message.deliveryStatus,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.messageClass,
+                                               message.timestamp,
+                                               message.read);
+
+      Services.obs.notifyObservers(domMessage,
+                                   kSilentSmsReceivedObserverTopic,
+                                   null);
+      return true;
+    }
+
+    // TODO: Bug #768441
+    // For now we don't store indicators persistently. When the mwi.discard
+    // flag is false, we'll need to persist the indicator to EFmwis.
+    // See TS 23.040 9.2.3.24.2
+
+    let mwi = message.mwi;
+    if (mwi) {
+      mwi.returnNumber = message.sender;
+      mwi.returnMessage = message.fullBody;
+      gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
+                                           this.clientId, mwi);
+      return true;
+    }
+
+    let notifyReceived = function notifyReceived(rv, domMessage) {
+      let success = Components.isSuccessCode(rv);
+
+      // Acknowledge the reception of the SMS.
+      this.workerMessenger.send("ackSMS", {
+        result: (success ? RIL.PDU_FCS_OK
+                         : RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
+      });
+
+      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 " + message.id + ", error code " + rv);
+        }
+        return;
+      }
+
+      this.broadcastSmsSystemMessage("sms-received", domMessage);
+      Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
+    }.bind(this);
+
+    if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
+      message.id = 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.delivery,
+                                               message.deliveryStatus,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.messageClass,
+                                               message.timestamp,
+                                               message.read);
+
+      notifyReceived(Cr.NS_OK, domMessage);
+    }
+
+    // SMS ACK will be sent in notifyReceived. Return false here.
+    return false;
+  },
+
+  /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     let data = this.rilContext.data;
     let apnSetting = this.apnSettings.byType.default;
     let dataCallConnected =
         (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED);
     if (apnSetting && datacall.ifname) {
@@ -1679,16 +1911,633 @@ RadioInterface.prototype = {
       if (response.success) {
         this._updateCallingLineIdRestrictionPref(response.clirMode);
       }
       target.sendAsyncMessage("RIL:SetCallingLineIdRestriction", response);
       return false;
     }).bind(this));
   },
 
+  /**
+   * List of tuples of national language identifier pairs.
+   *
+   * TODO: Support static/runtime settings, see bug 733331.
+   */
+  enabledGsmTableTuples: [
+    [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
+  ],
+
+  /**
+   * Use 16-bit reference number for concatenated outgoint messages.
+   *
+   * TODO: Support static/runtime settings, see bug 733331.
+   */
+  segmentRef16Bit: false,
+
+  /**
+   * Get valid SMS concatenation reference number.
+   */
+  _segmentRef: 0,
+  get nextSegmentRef() {
+    let ref = this._segmentRef++;
+
+    this._segmentRef %= (this.segmentRef16Bit ? 65535 : 255);
+
+    // 0 is not a valid SMS concatenation reference number.
+    return ref + 1;
+  },
+
+  /**
+   * Calculate encoded length using specified locking/single shift table
+   *
+   * @param message
+   *        message string to be encoded.
+   * @param langTable
+   *        locking shift table string.
+   * @param langShiftTable
+   *        single shift table string.
+   * @param strict7BitEncoding
+   *        Optional. Enable Latin characters replacement with corresponding
+   *        ones in GSM SMS 7-bit default alphabet.
+   *
+   * @return encoded length in septets.
+   *
+   * @note that the algorithm used in this function must match exactly with
+   * GsmPDUHelper#writeStringAsSeptets.
+   */
+  _countGsm7BitSeptets: function _countGsm7BitSeptets(message, langTable, langShiftTable, strict7BitEncoding) {
+    let length = 0;
+    for (let msgIndex = 0; msgIndex < message.length; msgIndex++) {
+      let c = message.charAt(msgIndex);
+      if (strict7BitEncoding) {
+        c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
+      }
+
+      let septet = langTable.indexOf(c);
+
+      // According to 3GPP TS 23.038, section 6.1.1 General notes, "The
+      // characters marked '1)' are not used but are displayed as a space."
+      if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
+        continue;
+      }
+
+      if (septet >= 0) {
+        length++;
+        continue;
+      }
+
+      septet = langShiftTable.indexOf(c);
+      if (septet < 0) {
+        if (!strict7BitEncoding) {
+          return -1;
+        }
+
+        // Bug 816082, when strict7BitEncoding is enabled, we should replace
+        // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
+        c = '*';
+        if (langTable.indexOf(c) >= 0) {
+          length++;
+        } else if (langShiftTable.indexOf(c) >= 0) {
+          length += 2;
+        } else {
+          // We can't even encode a '*' character with current configuration.
+          return -1;
+        }
+
+        continue;
+      }
+
+      // According to 3GPP TS 23.038 B.2, "This code represents a control
+      // character and therefore must not be used for language specific
+      // characters."
+      if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
+        continue;
+      }
+
+      // The character is not found in locking shfit table, but could be
+      // encoded as <escape><char> with single shift table. Note that it's
+      // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
+      // but we can display it as a space in this case as said in previous
+      // comment.
+      length += 2;
+    }
+
+    return length;
+  },
+
+  /**
+   * Calculate user data length of specified message string encoded in GSM 7Bit
+   * alphabets.
+   *
+   * @param message
+   *        a message string to be encoded.
+   * @param strict7BitEncoding
+   *        Optional. Enable Latin characters replacement with corresponding
+   *        ones in GSM SMS 7-bit default alphabet.
+   *
+   * @return null or an options object with attributes `dcs`,
+   *         `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`,
+   *         `langShiftIndex`, `segmentMaxSeq` set.
+   *
+   * @see #_calculateUserDataLength().
+   */
+  _calculateUserDataLength7Bit: function _calculateUserDataLength7Bit(message, strict7BitEncoding) {
+    let options = null;
+    let minUserDataSeptets = Number.MAX_VALUE;
+    for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
+      let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
+
+      const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
+      const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
+
+      let bodySeptets = this._countGsm7BitSeptets(message,
+                                                  langTable,
+                                                  langShiftTable,
+                                                  strict7BitEncoding);
+      if (bodySeptets < 0) {
+        continue;
+      }
+
+      let headerLen = 0;
+      if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
+        headerLen += 3; // IEI + len + langIndex
+      }
+      if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
+        headerLen += 3; // IEI + len + langShiftIndex
+      }
+
+      // Calculate full user data length, note the extra byte is for header len
+      let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
+      let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT;
+      if ((bodySeptets + headerSeptets) > segmentSeptets) {
+        headerLen += this.segmentRef16Bit ? 6 : 5;
+        headerSeptets = Math.ceil((headerLen + 1) * 8 / 7);
+        segmentSeptets -= headerSeptets;
+      }
+
+      let segments = Math.ceil(bodySeptets / segmentSeptets);
+      let userDataSeptets = bodySeptets + headerSeptets * segments;
+      if (userDataSeptets >= minUserDataSeptets) {
+        continue;
+      }
+
+      minUserDataSeptets = userDataSeptets;
+
+      options = {
+        dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET,
+        encodedFullBodyLength: bodySeptets,
+        userDataHeaderLength: headerLen,
+        langIndex: langIndex,
+        langShiftIndex: langShiftIndex,
+        segmentMaxSeq: segments,
+        segmentChars: segmentSeptets,
+      };
+    }
+
+    return options;
+  },
+
+  /**
+   * Calculate user data length of specified message string encoded in UCS2.
+   *
+   * @param message
+   *        a message string to be encoded.
+   *
+   * @return an options object with attributes `dcs`, `userDataHeaderLength`,
+   *         `encodedFullBodyLength`, `segmentMaxSeq` set.
+   *
+   * @see #_calculateUserDataLength().
+   */
+  _calculateUserDataLengthUCS2: function _calculateUserDataLengthUCS2(message) {
+    let bodyChars = message.length;
+    let headerLen = 0;
+    let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2);
+    let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2;
+    if ((bodyChars + headerChars) > segmentChars) {
+      headerLen += this.segmentRef16Bit ? 6 : 5;
+      headerChars = Math.ceil((headerLen + 1) / 2);
+      segmentChars -= headerChars;
+    }
+
+    let segments = Math.ceil(bodyChars / segmentChars);
+
+    return {
+      dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET,
+      encodedFullBodyLength: bodyChars * 2,
+      userDataHeaderLength: headerLen,
+      segmentMaxSeq: segments,
+      segmentChars: segmentChars,
+    };
+  },
+
+  /**
+   * Calculate user data length and its encoding.
+   *
+   * @param message
+   *        a message string to be encoded.
+   * @param strict7BitEncoding
+   *        Optional. Enable Latin characters replacement with corresponding
+   *        ones in GSM SMS 7-bit default alphabet.
+   *
+   * @return an options object with some or all of following attributes set:
+   *
+   * @param dcs
+   *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
+   *        constants.
+   * @param userDataHeaderLength
+   *        Length of embedded user data header, in bytes. The whole header
+   *        size will be userDataHeaderLength + 1; 0 for no header.
+   * @param encodedFullBodyLength
+   *        Length of the message body 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 segmentMaxSeq
+   *        Max sequence number of a multi-part messages, or 1 for single one.
+   *        This number might not be accurate for a multi-part message until
+   *        it's processed by #_fragmentText() again.
+   */
+  _calculateUserDataLength: function _calculateUserDataLength(message, strict7BitEncoding) {
+    let options = this._calculateUserDataLength7Bit(message, strict7BitEncoding);
+    if (!options) {
+      options = this._calculateUserDataLengthUCS2(message);
+    }
+
+    if (DEBUG) this.debug("_calculateUserDataLength: " + JSON.stringify(options));
+    return options;
+  },
+
+  /**
+   * Fragment GSM 7-Bit encodable string for transmission.
+   *
+   * @param text
+   *        text string to be fragmented.
+   * @param langTable
+   *        locking shift table string.
+   * @param langShiftTable
+   *        single shift table string.
+   * @param segmentSeptets
+   *        Number of available spetets per segment.
+   * @param strict7BitEncoding
+   *        Optional. Enable Latin characters replacement with corresponding
+   *        ones in GSM SMS 7-bit default alphabet.
+   *
+   * @return an array of objects. See #_fragmentText() for detailed definition.
+   */
+  _fragmentText7Bit: function _fragmentText7Bit(text, langTable, langShiftTable, segmentSeptets, strict7BitEncoding) {
+    let ret = [];
+    let body = "", len = 0;
+    for (let i = 0, inc = 0; i < text.length; i++) {
+      let c = text.charAt(i);
+      if (strict7BitEncoding) {
+        c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
+      }
+
+      let septet = langTable.indexOf(c);
+      if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
+        continue;
+      }
+
+      if (septet >= 0) {
+        inc = 1;
+      } else {
+        septet = langShiftTable.indexOf(c);
+        if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
+          continue;
+        }
+
+        inc = 2;
+        if (septet < 0) {
+          if (!strict7BitEncoding) {
+            throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!");
+          }
+
+          // Bug 816082, when strict7BitEncoding is enabled, we should replace
+          // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
+          c = '*';
+          if (langTable.indexOf(c) >= 0) {
+            inc = 1;
+          }
+        }
+      }
+
+      if ((len + inc) > segmentSeptets) {
+        ret.push({
+          body: body,
+          encodedBodyLength: len,
+        });
+        body = c;
+        len = inc;
+      } else {
+        body += c;
+        len += inc;
+      }
+    }
+
+    if (len) {
+      ret.push({
+        body: body,
+        encodedBodyLength: len,
+      });
+    }
+
+    return ret;
+  },
+
+  /**
+   * Fragment UCS2 encodable string for transmission.
+   *
+   * @param text
+   *        text string to be fragmented.
+   * @param segmentChars
+   *        Number of available characters per segment.
+   *
+   * @return an array of objects. See #_fragmentText() for detailed definition.
+   */
+  _fragmentTextUCS2: function _fragmentTextUCS2(text, segmentChars) {
+    let ret = [];
+    for (let offset = 0; offset < text.length; offset += segmentChars) {
+      let str = text.substr(offset, segmentChars);
+      ret.push({
+        body: str,
+        encodedBodyLength: str.length * 2,
+      });
+    }
+
+    return ret;
+  },
+
+  /**
+   * Fragment string for transmission.
+   *
+   * Fragment input text string into an array of objects that contains
+   * attributes `body`, substring for this segment, `encodedBodyLength`,
+   * length of the encoded segment body in septets.
+   *
+   * @param text
+   *        Text string to be fragmented.
+   * @param options
+   *        Optional pre-calculated option object. The output array will be
+   *        stored at options.segments if there are multiple segments.
+   * @param strict7BitEncoding
+   *        Optional. Enable Latin characters replacement with corresponding
+   *        ones in GSM SMS 7-bit default alphabet.
+   *
+   * @return Populated options object.
+   */
+  _fragmentText: function _fragmentText(text, options, strict7BitEncoding) {
+    if (!options) {
+      options = this._calculateUserDataLength(text, strict7BitEncoding);
+    }
+
+    if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+      const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex];
+      const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex];
+      options.segments = this._fragmentText7Bit(text,
+                                                langTable, langShiftTable,
+                                                options.segmentChars,
+                                                strict7BitEncoding);
+    } else {
+      options.segments = this._fragmentTextUCS2(text,
+                                                options.segmentChars);
+    }
+
+    // Re-sync options.segmentMaxSeq with actual length of returning array.
+    options.segmentMaxSeq = options.segments.length;
+
+    return options;
+  },
+
+  getSegmentInfoForText: function getSegmentInfoForText(text, request) {
+    let strict7BitEncoding;
+    try {
+      strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
+    } catch (e) {
+      strict7BitEncoding = false;
+    }
+
+    let options = this._fragmentText(text, null, strict7BitEncoding);
+    let charsInLastSegment;
+    if (options.segmentMaxSeq) {
+      let lastSegment = options.segments[options.segmentMaxSeq - 1];
+      charsInLastSegment = lastSegment.encodedBodyLength;
+      if (options.dcs == RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET) {
+        // In UCS2 encoding, encodedBodyLength is in octets.
+        charsInLastSegment /= 2;
+      }
+    } else {
+      charsInLastSegment = 0;
+    }
+
+    let result = gMobileMessageService
+                 .createSmsSegmentInfo(options.segmentMaxSeq,
+                                       options.segmentChars,
+                                       options.segmentChars - charsInLastSegment);
+    request.notifySegmentInfoForTextGot(result);
+  },
+
+  sendSMS: function sendSMS(number, message, silent, request) {
+    let strict7BitEncoding;
+    try {
+      strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
+    } catch (e) {
+      strict7BitEncoding = false;
+    }
+
+    let options = this._fragmentText(message, null, strict7BitEncoding);
+    options.number = PhoneNumberUtils.normalize(number);
+    let requestStatusReport;
+    try {
+      requestStatusReport =
+        Services.prefs.getBoolPref("dom.sms.requestStatusReport");
+    } catch (e) {
+      requestStatusReport = true;
+    }
+    options.requestStatusReport = requestStatusReport && !silent;
+    if (options.segmentMaxSeq > 1) {
+      options.segmentRef16Bit = this.segmentRef16Bit;
+      options.segmentRef = this.nextSegmentRef;
+    }
+
+    let notifyResult = (function notifyResult(rv, domMessage) {
+      // TODO bug 832140 handle !Components.isSuccessCode(rv)
+      if (!silent) {
+        Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
+      }
+
+      // If the radio is disabled or the SIM card is not ready, just directly
+      // return with the corresponding error code.
+      let errorCode;
+      if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
+        if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
+                              options.number);
+        errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
+      } else if (!this._radioEnabled) {
+        if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
+        errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
+      } else if (this.rilContext.cardState != "ready") {
+        if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS.");
+        errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
+      }
+      if (errorCode) {
+        if (silent) {
+          request.notifySendMessageFailed(errorCode);
+          return;
+        }
+
+        gMobileMessageDatabaseService
+          .setMessageDeliveryByMessageId(domMessage.id,
+                                         null,
+                                         DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
+                                         RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
+                                         null,
+                                         function notifyResult(rv, domMessage) {
+          // TODO bug 832140 handle !Components.isSuccessCode(rv)
+          request.notifySendMessageFailed(errorCode);
+          Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
+        });
+        return;
+      }
+
+      // Keep current SMS message info for sent/delivered notifications
+      let context = {
+        request: request,
+        sms: domMessage,
+        requestStatusReport: options.requestStatusReport,
+        silent: silent
+      };
+
+      // This is the entry point starting to send SMS.
+      this.workerMessenger.send("sendSMS", options,
+                                (function(context, response) {
+        if (response.errorMsg) {
+          // Failed to send SMS out.
+          let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR;
+          switch (response.errorMsg) {
+            case RIL.ERROR_RADIO_NOT_AVAILABLE:
+              error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
+              break;
+          }
+
+          if (context.silent) {
+            context.request.notifySendMessageFailed(error);
+            return false;
+          }
+
+          gMobileMessageDatabaseService
+            .setMessageDeliveryByMessageId(context.sms.id,
+                                           null,
+                                           DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
+                                           RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
+                                           null,
+                                           function notifyResult(rv, domMessage) {
+            // TODO bug 832140 handle !Components.isSuccessCode(rv)
+            context.request.notifySendMessageFailed(error);
+            Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
+          });
+          return false;
+        } // End of send failure.
+
+        if (response.deliveryStatus) {
+          // Message delivery.
+          gMobileMessageDatabaseService
+            .setMessageDeliveryByMessageId(context.sms.id,
+                                           null,
+                                           context.sms.delivery,
+                                           response.deliveryStatus,
+                                           null,
+                                           function notifyResult(rv, domMessage) {
+            // TODO bug 832140 handle !Components.isSuccessCode(rv)
+            let topic = (response.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
+                        ? kSmsDeliverySuccessObserverTopic
+                        : kSmsDeliveryErrorObserverTopic;
+            Services.obs.notifyObservers(domMessage, topic, null);
+          });
+
+          // Send transaction has ended completely.
+          return false;
+        } // End of message delivery.
+
+        // Message sent.
+        if (context.silent) {
+          // There is no way to modify nsIDOMMozSmsMessage attributes as they are
+          // read only so we just create a new sms instance to send along with
+          // the notification.
+          let sms = context.sms;
+          context.request.notifyMessageSent(
+            gMobileMessageService.createSmsMessage(sms.id,
+                                                   sms.threadId,
+                                                   DOM_MOBILE_MESSAGE_DELIVERY_SENT,
+                                                   sms.deliveryStatus,
+                                                   sms.sender,
+                                                   sms.receiver,
+                                                   sms.body,
+                                                   sms.messageClass,
+                                                   sms.timestamp,
+                                                   sms.read));
+          // We don't wait for SMS-DELIVER-REPORT for silent one.
+          return false;
+        }
+
+        gMobileMessageDatabaseService
+          .setMessageDeliveryByMessageId(context.sms.id,
+                                         null,
+                                         DOM_MOBILE_MESSAGE_DELIVERY_SENT,
+                                         context.sms.deliveryStatus,
+                                         null,
+                                         (function notifyResult(rv, domMessage) {
+          // TODO bug 832140 handle !Components.isSuccessCode(rv)
+          this.broadcastSmsSystemMessage("sms-sent", domMessage);
+
+          if (context.requestStatusReport) {
+            context.sms = domMessage;
+          }
+
+          context.request.notifyMessageSent(domMessage);
+          Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null);
+        }).bind(this));
+
+        // Only keep current context if we have requested for delivery report.
+        return context.requestStatusReport;
+      }).bind(this, context)); // End of |workerMessenger.send| callback.
+    }).bind(this); // End of DB saveSendingMessage callback.
+
+    let sendingMessage = {
+      type: "sms",
+      sender: this.getMsisdn(),
+      receiver: number,
+      body: message,
+      deliveryStatusRequested: options.requestStatusReport,
+      timestamp: Date.now()
+    };
+
+    if (silent) {
+      let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING;
+      let delivery = DOM_MOBILE_MESSAGE_DELIVERY_SENDING;
+      let domMessage =
+        gMobileMessageService.createSmsMessage(-1, // id
+                                               0,  // threadId
+                                               delivery,
+                                               deliveryStatus,
+                                               sendingMessage.sender,
+                                               sendingMessage.receiver,
+                                               sendingMessage.body,
+                                               "normal", // message class
+                                               sendingMessage.timestamp,
+                                               false);
+      notifyResult(Cr.NS_OK, domMessage);
+      return;
+    }
+
+    let id = gMobileMessageDatabaseService.saveSendingMessage(
+      sendingMessage, notifyResult);
+  },
+
   registerDataCallCallback: function registerDataCallCallback(callback) {
     if (this._datacall_callbacks) {
       if (this._datacall_callbacks.indexOf(callback) != -1) {
         throw new Error("Already registered this callback!");
       }
     } else {
       this._datacall_callbacks = [];
     }
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -1,16 +1,17 @@
 /* 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 "nsISupports.idl"
 
 interface nsIDOMMozIccInfo;
 interface nsIDOMMozMobileConnectionInfo;
+interface nsIMobileMessageCallback;
 
 [scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)]
 interface nsIRILDataCallInfo : nsISupports
 {
   /**
    * Current data call state, one of the
    * nsINetworkInterface::NETWORK_STATE_* constants.
    */
@@ -78,33 +79,44 @@ interface nsIRilContext : nsISupports
 };
 
 [scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
 interface nsIRilSendWorkerMessageCallback : nsISupports
 {
   boolean handleResponse(in jsval response);
 };
 
-[scriptable, uuid(b1af7aad-6547-427c-a878-e2ebf98a14d6)]
+[scriptable, uuid(61a8ca67-6113-4cd0-b443-e045f09863ed)]
 interface nsIRadioInterface : nsISupports
 {
   readonly attribute nsIRilContext rilContext;
 
   /**
    * PDP APIs
    */
   void setupDataCallByType(in DOMString apntype);
   void deactivateDataCallByType(in DOMString apntype);
   long getDataCallStateByType(in DOMString apntype);
 
   void registerDataCallCallback(in nsIRILDataCallback callback);
   void unregisterDataCallCallback(in nsIRILDataCallback callback);
 
   void updateRILNetworkInterface();
 
+  /**
+   * SMS-related functionality.
+   */
+  void getSegmentInfoForText(in DOMString text,
+                             in nsIMobileMessageCallback request);
+
+  void sendSMS(in DOMString number,
+               in DOMString message,
+               in boolean silent,
+               in nsIMobileMessageCallback request);
+
   void sendWorkerMessage(in DOMString type,
               [optional] in jsval message,
               [optional] in nsIRilSendWorkerMessageCallback callback);
 };
 
 [scriptable, uuid(44b03951-1444-4c03-bd37-0bcb3a01b56f)]
 interface nsIRadioInterfaceLayer : nsISupports
 {