Merge B2g-inbound to Mozilla-Central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 25 Oct 2013 11:48:38 +0200
changeset 166894 9f8233fcce1d3f0676bc720303dc7bbd7e246c13
parent 166869 dff9376142682cf42e11dcbebaa28c123f2ceabc (current diff)
parent 166893 94e90235eee0d8265cbebe807958058ddb4144f1 (diff)
child 166907 85ade1df059788ab9fd68611302688ea2fcdb9e3
child 166969 26d96c3c038044c4224bc36d1cad179737fd3f18
child 167000 1e0e616739c9b7a1a69dc1592c6383a407c6dba4
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
9f8233fcce1d / 27.0a1 / 20131025030239 / files
nightly linux64
9f8233fcce1d / 27.0a1 / 20131025030239 / files
nightly mac
9f8233fcce1d / 27.0a1 / 20131025030239 / files
nightly win32
9f8233fcce1d / 27.0a1 / 20131025030239 / files
nightly win64
9f8233fcce1d / 27.0a1 / 20131025030239 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge B2g-inbound to Mozilla-Central
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -152,16 +152,26 @@ SettingsListener.observe('language.curre
     function(value) {
       Services.prefs.setCharPref('wap.UAProf.url', value);
   });
 
   SettingsListener.observe('wap.UAProf.tagname', 'x-wap-profile',
     function(value) {
       Services.prefs.setCharPref('wap.UAProf.tagname', value);
   });
+
+  // DSDS default service IDs
+  ['mms', 'sms', 'telephony', 'voicemail'].forEach(function(key) {
+    SettingsListener.observe('ril.' + key + '.defaultServiceId', 0,
+                             function(value) {
+      if (value != null) {
+        Services.prefs.setIntPref('dom.' + key + '.defaultServiceId', value);
+      }
+    });
+  });
 })();
 
 //=================== DeviceInfo ====================
 Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
 Components.utils.import('resource://gre/modules/ctypes.jsm');
 (function DeviceInfoToSettings() {
   // MOZ_B2G_VERSION is set in b2g/confvars.sh, and is output as a #define value
   // from configure.in, defaults to 1.0.0 if this value is not exist.
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1322,26 +1322,16 @@ window.addEventListener('ContentStart', 
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
       type: 'volume-state-changed',
       active: (aData == 'Shared')
     });
 }, 'volume-state-changed', false);
 })();
 
-Services.obs.addObserver(function(aSubject, aTopic, aData) {
-  let data = JSON.parse(aData);
-  shell.sendChromeEvent({
-    type: "activity-done",
-    success: data.success,
-    manifestURL: data.manifestURL,
-    pageURL: data.pageURL
-  });
-}, "activity-done", false);
-
 #ifdef MOZ_WIDGET_GONK
 // Devices don't have all the same partition size for /cache where we
 // store the http cache.
 (function setHTTPCacheSize() {
   let path = Services.prefs.getCharPref("browser.cache.disk.parent_directory");
   let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
                         .getService(Ci.nsIVolumeService);
 
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "20e3f42ccb6073c6d9bc9741de3a19a939a8a7d8", 
+    "revision": "e3564b8656fcd189d4a83379e94bef1ab7d657b5", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/activities/src/ActivitiesService.jsm
+++ b/dom/activities/src/ActivitiesService.jsm
@@ -296,22 +296,20 @@ let Activities = {
         this.callers[msg.id] = { mm: aMessage.target,
                                  manifestURL: msg.manifestURL,
                                  pageURL: msg.pageURL };
         this.startActivity(msg);
         break;
 
       case "Activity:PostResult":
         caller.mm.sendAsyncMessage("Activity:FireSuccess", msg);
-        Services.obs.notifyObservers(null, "activity-done", obsData);
         delete this.callers[msg.id];
         break;
       case "Activity:PostError":
         caller.mm.sendAsyncMessage("Activity:FireError", msg);
-        Services.obs.notifyObservers(null, "activity-done", obsData);
         delete this.callers[msg.id];
         break;
 
       case "Activities:Register":
         let self = this;
         this.db.add(msg,
           function onSuccess(aEvent) {
             mm.sendAsyncMessage("Activities:Register:OK", null);
--- a/dom/activities/src/ActivityRequestHandler.js
+++ b/dom/activities/src/ActivityRequestHandler.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 function debug(aMsg) {
   //dump("-- ActivityRequestHandler.js " + Date.now() + " : " + aMsg + "\n");
 }
@@ -48,23 +49,25 @@ ActivityRequestHandler.prototype = {
     return this._options;
   },
 
   postResult: function arh_postResult(aResult) {
     cpmm.sendAsyncMessage("Activity:PostResult", {
       "id": this._id,
       "result": aResult
     });
+    Services.obs.notifyObservers(null, "activity-success", this._id);
   },
 
   postError: function arh_postError(aError) {
     cpmm.sendAsyncMessage("Activity:PostError", {
       "id": this._id,
       "error": aError
     });
+    Services.obs.notifyObservers(null, "activity-error", this._id);
   },
 
   classID: Components.ID("{9326952a-dbe3-4d81-a51f-d9c160d96d6b}"),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMMozActivityRequestHandler
   ]),
 
--- a/dom/activities/src/ActivityWrapper.js
+++ b/dom/activities/src/ActivityWrapper.js
@@ -45,31 +45,46 @@ ActivityWrapper.prototype = {
     // We don't need to check whether the activity itself already sent
     // back something since ActivitiesService.jsm takes care of that.
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
     let innerWindowID = util.currentInnerWindowID;
 
     let observer = {
       observe: function(aSubject, aTopic, aData) {
-        if (aTopic !== "inner-window-destroyed") {
-          return;
-        }
 
-        let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-        if (wId == innerWindowID) {
-          debug("Closing activity window " + innerWindowID);
-          Services.obs.removeObserver(observer, "inner-window-destroyed");
-          cpmm.sendAsyncMessage("Activity:PostError",
-                                { id: aMessage.id,
-                                  error: "ActivityCanceled" });
+        switch (aTopic) {
+          case 'inner-window-destroyed':
+            let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+            if (wId == innerWindowID) {
+              debug("Closing activity window " + innerWindowID);
+              Services.obs.removeObserver(observer, "inner-window-destroyed");
+              cpmm.sendAsyncMessage("Activity:PostError",
+                                    { id: aMessage.id,
+                                      error: "ActivityCanceled"
+                                    });
+            }
+            break;
+          case 'activity-error':
+          case 'activity-success':
+            if (aData !== aMessage.id) {
+              return;
+            }
+            Services.obs.removeObserver(observer, "activity-error");
+            Services.obs.removeObserver(observer, "activity-success");
+            let docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIWebNavigation);
+            Services.obs.notifyObservers(docshell, "activity-done", aTopic);
+            break;
         }
       }
     }
 
+    Services.obs.addObserver(observer, "activity-error", false);
+    Services.obs.addObserver(observer, "activity-success", false);
     Services.obs.addObserver(observer, "inner-window-destroyed", false);
     return handler;
   },
 
   classID: Components.ID("{5430d6f9-32d6-4924-ba39-6b6d1b093cd6}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
 }
 
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -260,33 +260,42 @@ BrowserElementChild.prototype = {
 
     Services.obs.addObserver(this,
                              'ask-parent-to-rollback-fullscreen',
                              /* ownsWeak = */ true);
 
     Services.obs.addObserver(this,
                              'xpcom-shutdown',
                              /* ownsWeak = */ true);
+
+    Services.obs.addObserver(this,
+                             'activity-done',
+                             /* ownsWeak = */ true);
   },
 
   observe: function(subject, topic, data) {
     // Ignore notifications not about our document.  (Note that |content| /can/
     // be null; see bug 874900.)
-    if (!content || subject != content.document)
+    if (topic !== 'activity-done' && (!content || subject != content.document))
+      return;
+    if (topic == 'activity-done' && docShell !== subject)
       return;
     switch (topic) {
       case 'fullscreen-origin-change':
         sendAsyncMsg('fullscreen-origin-change', { _payload_: data });
         break;
       case 'ask-parent-to-exit-fullscreen':
         sendAsyncMsg('exit-fullscreen');
         break;
       case 'ask-parent-to-rollback-fullscreen':
         sendAsyncMsg('rollback-fullscreen');
         break;
+      case 'activity-done':
+        sendAsyncMsg('activitydone', { success: (data == 'activity-success') });
+        break;
       case 'xpcom-shutdown':
         this._shuttingDown = true;
         break;
     }
   },
 
   /**
    * Called when our TabChildGlobal starts to die.  This is not called when the
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -122,16 +122,17 @@ function BrowserElementParent(frameLoade
     "contextmenu": this._fireCtxMenuEvent,
     "locationchange": this._fireEventFromMsg,
     "loadstart": this._fireEventFromMsg,
     "loadend": this._fireEventFromMsg,
     "titlechange": this._fireEventFromMsg,
     "iconchange": this._fireEventFromMsg,
     "close": this._fireEventFromMsg,
     "resize": this._fireEventFromMsg,
+    "activitydone": this._fireEventFromMsg,
     "opensearch": this._fireEventFromMsg,
     "securitychange": this._fireEventFromMsg,
     "error": this._fireEventFromMsg,
     "scroll": this._fireEventFromMsg,
     "firstpaint": this._fireEventFromMsg,
     "documentfirstpaint": this._fireEventFromMsg,
     "nextpaint": this._recvNextPaint,
     "keyevent": this._fireKeyEvent,
--- a/dom/mobilemessage/interfaces/nsIMmsService.idl
+++ b/dom/mobilemessage/interfaces/nsIMmsService.idl
@@ -7,17 +7,19 @@ interface nsIMobileMessageCallback;
 interface nsIDOMBlob;
 
 %{C++
 #define MMS_SERVICE_CID { 0x06d9124b, 0x80e0, 0x40ed, \
   { 0x98, 0x71, 0x4d, 0x23, 0x4a, 0x0f, 0xd4, 0x31 } }
 #define MMS_SERVICE_CONTRACTID "@mozilla.org/mms/mmsservice;1"
 %}
 
-[scriptable, uuid(e5ef630a-eab7-425a-ac42-650ef5c4fcef)]
+[scriptable, uuid(5dc8b3bc-c3a9-45ea-8ee0-7562b0e57257)]
 interface nsIMmsService : nsISupports
 {
+  readonly attribute unsigned long mmsDefaultServiceId;
+
   void send(in jsval parameters /* MmsParameters */,
             in nsIMobileMessageCallback request);
 
   void retrieve(in long id,
                 in nsIMobileMessageCallback request);
 };
--- a/dom/mobilemessage/interfaces/nsISmsService.idl
+++ b/dom/mobilemessage/interfaces/nsISmsService.idl
@@ -8,19 +8,21 @@ 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, builtinclass, uuid(0f3f75ec-00dd-11e3-87ac-0b1d5c79afdf)]
+[scriptable, builtinclass, uuid(ec3221fb-2d4a-4ccd-ac64-65c1b2dee5dd)]
 interface nsISmsService : nsISupports
 {
+  readonly attribute unsigned long smsDefaultServiceId;
+
   boolean hasSupport();
 
   void getSegmentInfoForText(in DOMString text,
                              in nsIMobileMessageCallback request);
 
   void send(in DOMString number,
             in DOMString message,
             in boolean silent,
--- a/dom/mobilemessage/src/android/MmsService.cpp
+++ b/dom/mobilemessage/src/android/MmsService.cpp
@@ -10,16 +10,23 @@
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(MmsService, nsIMmsService)
 
 NS_IMETHODIMP
+MmsService::GetMmsDefaultServiceId(uint32_t* aServiceId)
+{
+  NS_NOTYETIMPLEMENTED("Implement me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 MmsService::Send(const JS::Value& aParameters,
                  nsIMobileMessageCallback *aRequest)
 {
   NS_NOTYETIMPLEMENTED("Implement me!");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
--- a/dom/mobilemessage/src/android/SmsService.cpp
+++ b/dom/mobilemessage/src/android/SmsService.cpp
@@ -11,16 +11,24 @@
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
 
 NS_IMETHODIMP
+SmsService::GetSmsDefaultServiceId(uint32_t* aServiceId)
+{
+  // Android has no official DSDS support.
+  *aServiceId = 0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 SmsService::HasSupport(bool* aHasSupport)
 {
   *aHasSupport = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SmsService::GetSegmentInfoForText(const nsAString& aText,
--- a/dom/mobilemessage/src/fallback/MmsService.cpp
+++ b/dom/mobilemessage/src/fallback/MmsService.cpp
@@ -9,16 +9,23 @@
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(MmsService, nsIMmsService)
 
 NS_IMETHODIMP
+MmsService::GetMmsDefaultServiceId(uint32_t* aServiceId)
+{
+  NS_NOTYETIMPLEMENTED("Implement me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 MmsService::Send(const JS::Value& aParameters,
                  nsIMobileMessageCallback *aRequest)
 {
   NS_NOTYETIMPLEMENTED("Implement me!");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
--- a/dom/mobilemessage/src/fallback/SmsService.cpp
+++ b/dom/mobilemessage/src/fallback/SmsService.cpp
@@ -10,16 +10,23 @@
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
 
 NS_IMETHODIMP
+SmsService::GetSmsDefaultServiceId(uint32_t* aServiceId)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 SmsService::HasSupport(bool* aHasSupport)
 {
   *aHasSupport = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SmsService::GetSegmentInfoForText(const nsAString& aText,
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -28,19 +28,18 @@ try {
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSmsRetrievingObserverTopic        = "sms-retrieving";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
-const kXpcomShutdownObserverTopic        = "xpcom-shutdown";
-const kPrefenceChangedObserverTopic      = "nsPref:changed";
 const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
 
 // HTTP status codes:
 // @see http://tools.ietf.org/html/rfc2616#page-39
 const HTTP_STATUS_OK = 200;
 
 // Non-standard HTTP status for internal use.
 const _HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS = 0;
@@ -54,27 +53,28 @@ const _MMS_ERROR_MESSAGE_DELETED = -1;
 const _MMS_ERROR_RADIO_DISABLED = -2;
 const _MMS_ERROR_NO_SIM_CARD = -3;
 
 const CONFIG_SEND_REPORT_NEVER       = 0;
 const CONFIG_SEND_REPORT_DEFAULT_NO  = 1;
 const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
 const CONFIG_SEND_REPORT_ALWAYS      = 3;
 
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
 const TIME_TO_BUFFER_MMS_REQUESTS    = 30000;
 const PREF_TIME_TO_RELEASE_MMS_CONNECTION =
   Services.prefs.getIntPref("network.gonk.ms-release-mms-connection");
 
-const PREF_RETRIEVAL_MODE      = 'dom.mms.retrieval_mode';
+const kPrefRetrievalMode       = 'dom.mms.retrieval_mode';
 const RETRIEVAL_MODE_MANUAL    = "manual";
 const RETRIEVAL_MODE_AUTOMATIC = "automatic";
 const RETRIEVAL_MODE_AUTOMATIC_HOME = "automatic-home";
 const RETRIEVAL_MODE_NEVER     = "never";
 
-
 //Internal const values.
 const DELIVERY_RECEIVED       = "received";
 const DELIVERY_NOT_DOWNLOADED = "not-downloaded";
 const DELIVERY_SENDING        = "sending";
 const DELIVERY_SENT           = "sent";
 const DELIVERY_ERROR          = "error";
 
 const DELIVERY_STATUS_SUCCESS        = "success";
@@ -103,16 +103,19 @@ const PREF_RETRIEVAL_RETRY_INTERVALS = (
     if (!intervals[i]) {
       intervals[i] = 600000;
     }
   }
   intervals.length = PREF_RETRIEVAL_RETRY_COUNT;
   return intervals;
 })();
 
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+const kPrefDefaultServiceId = "dom.mms.defaultServiceId";
+
 XPCOMUtils.defineLazyServiceGetter(this, "gpps",
                                    "@mozilla.org/network/protocol-proxy-service;1",
                                    "nsIProtocolProxyService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
@@ -192,17 +195,17 @@ XPCOMUtils.defineLazyGetter(this, "gMmsC
       if (this.connected) {
         gRadioInterface.deactivateDataCallByType("mms");
       }
     },
 
     init: function init() {
       Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic,
                                false);
-      Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
+      Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
       this.settings.forEach(function(name) {
         Services.prefs.addObserver(name, this, false);
       }, this);
 
       try {
         this.mmsc = Services.prefs.getCharPref("ril.mms.mmsc");
         this.proxy = Services.prefs.getCharPref("ril.mms.mmsproxy");
         this.port = Services.prefs.getIntPref("ril.mms.mmsport");
@@ -336,20 +339,19 @@ XPCOMUtils.defineLazyGetter(this, "gMmsC
     clearMmsProxySettings: function clearMmsProxySettings() {
       this.mmsc = null;
       this.proxy = null;
       this.port = null;
       this.proxyInfo = null;
     },
 
     shutdown: function shutdown() {
+      Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
       Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
-      this.settings.forEach(function(name) {
-        Services.prefs.removeObserver(name, this);
-      }, this);
+
       this.connectTimer.cancel();
       this.flushPendingCallbacks(_HTTP_STATUS_RADIO_DISABLED);
       this.disconnectTimer.cancel();
       this.onDisconnectTimerTimeout();
     },
 
     // nsIObserver
 
@@ -365,17 +367,17 @@ XPCOMUtils.defineLazyGetter(this, "gMmsC
           }
 
           if (DEBUG) debug("Got the MMS network connected! Resend the buffered " +
                            "MMS requests: number: " + this.pendingCallbacks.length);
           this.connectTimer.cancel();
           this.flushPendingCallbacks(_HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS)
           break;
         }
-        case kPrefenceChangedObserverTopic: {
+        case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: {
           if (data == "ril.radio.disabled") {
             try {
               this.radioDisabled = Services.prefs.getBoolPref("ril.radio.disabled");
             } catch (e) {
               if (DEBUG) debug("Updating preference 'ril.radio.disabled' fails.");
               this.radioDisabled = false;
             }
             return;
@@ -399,18 +401,17 @@ XPCOMUtils.defineLazyGetter(this, "gMmsC
             }
           } catch (e) {
             if (DEBUG) debug("Failed to update the MMS proxy settings from the" +
                              "preference.");
             this.clearMmsProxySettings();
           }
           break;
         }
-        case kXpcomShutdownObserverTopic: {
-          Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
+        case NS_XPCOM_SHUTDOWN_OBSERVER_ID: {
           this.shutdown();
         }
       }
     }
   };
   conn.init();
 
   return conn;
@@ -752,28 +753,28 @@ CancellableTransaction.prototype = {
   // Keep a reference to the callback when calling
   // |[Send|Retrieve]Transaction.run(callback)|.
   runCallback: null,
 
   isObserversAdded: false,
 
   registerRunCallback: function registerRunCallback(callback) {
     if (!this.isObserversAdded) {
-      Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
+      Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
       Services.obs.addObserver(this, kMobileMessageDeletedObserverTopic, false);
       this.isObserversAdded = true;
     }
 
     this.runCallback = callback;
     this.isCancelled = false;
   },
 
   removeObservers: function removeObservers() {
     if (this.isObserversAdded) {
-      Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
+      Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
       Services.obs.removeObserver(this, kMobileMessageDeletedObserverTopic);
       this.isObserversAdded = false;
     }
   },
 
   runCallbackIfValid: function runCallbackIfValid(mmsStatus, msg) {
     this.removeObservers();
 
@@ -806,17 +807,17 @@ CancellableTransaction.prototype = {
       this.cancellable = null;
     }
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     switch (topic) {
-      case kXpcomShutdownObserverTopic: {
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID: {
         this.cancelRunning();
         break;
       }
       case kMobileMessageDeletedObserverTopic: {
         data = JSON.parse(data);
         if (data.id != this.cancellableId) {
           return;
         }
@@ -1186,33 +1187,48 @@ AcknowledgeTransaction.prototype = {
         callback(httpStatus);
       };
     }
     gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
                                       this.istream, requestCallback);
   }
 };
 
+function getDefaultServiceId() {
+  let id = Services.prefs.getIntPref(kPrefDefaultServiceId);
+  let numRil = Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
+
+  if (id >= numRil || id < 0) {
+    id = 0;
+  }
+
+  return id;
+}
+
 /**
  * MmsService
  */
 function MmsService() {
   if (DEBUG) {
     let macro = (MMS.MMS_VERSION >> 4) & 0x0f;
     let minor = MMS.MMS_VERSION & 0x0f;
     debug("Running protocol version: " + macro + "." + minor);
   }
 
+  Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
+  this.mmsDefaultServiceId = getDefaultServiceId();
+
   // TODO: bug 810084 - support application identifier
 }
 MmsService.prototype = {
 
   classID:   RIL_MMSSERVICE_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMmsService,
-                                         Ci.nsIWapPushApplication]),
+                                         Ci.nsIWapPushApplication,
+                                         Ci.nsIObserver]),
   /*
    * Whether or not should we enable X-Mms-Report-Allowed in M-NotifyResp.ind
    * and M-Acknowledge.ind PDU.
    */
   confSendDeliveryReport: CONFIG_SEND_REPORT_DEFAULT_YES,
 
   /**
    * Calculate Whether or not should we enable X-Mms-Report-Allowed.
@@ -1579,17 +1595,17 @@ MmsService.prototype = {
           && aMessageRecord) {
         if (DEBUG) debug("We already got the NotificationIndication with transactionId = "
                          + transactionId + " before.");
         return;
       }
 
       let retrievalMode = RETRIEVAL_MODE_MANUAL;
       try {
-        retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
+        retrievalMode = Services.prefs.getCharPref(kPrefRetrievalMode);
       } catch (e) {}
 
       let savableMessage = this.convertIntermediateToSavable(notification, retrievalMode);
 
       gMobileMessageDatabaseService
         .saveReceivedMessage(savableMessage,
                              this.saveReceivedMessageCallback.bind(this,
                                                                    retrievalMode,
@@ -1798,16 +1814,18 @@ MmsService.prototype = {
                      JSON.stringify(aMessage));
 
     return isAddrValid ? Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR
                        : Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
   },
 
   // nsIMmsService
 
+  mmsDefaultServiceId: 0,
+
   send: function send(aParams, aRequest) {
     if (DEBUG) debug("send: aParams: " + JSON.stringify(aParams));
 
     // Note that the following sanity checks for |aParams| should be consistent
     // with the checks in SmsIPCService.GetSendMmsMessageRequestFromParams.
 
     // Check if |aParams| is valid.
     if (aParams == null || typeof aParams != "object") {
@@ -2108,16 +2126,28 @@ MmsService.prototype = {
       case MMS.MMS_PDU_TYPE_DELIVERY_IND:
         this.handleDeliveryIndication(msg);
         break;
       default:
         if (DEBUG) debug("Unsupported X-MMS-Message-Type: " + msg.type);
         break;
     }
   },
+
+  // nsIObserver
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (aData === kPrefDefaultServiceId) {
+          this.mmsDefaultServiceId = getDefaultServiceId();
+        }
+        break;
+    }
+  }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MmsService]);
 
 let debug;
 if (DEBUG) {
   debug = function (s) {
     dump("-@- MmsService: " + s + "\n");
--- a/dom/mobilemessage/src/gonk/SmsService.cpp
+++ b/dom/mobilemessage/src/gonk/SmsService.cpp
@@ -2,31 +2,94 @@
 /* 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"
+#include "mozilla/Preferences.h"
 #include "nsServiceManagerUtils.h"
 
+namespace {
+
+const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+#define kPrefDefaultServiceId "dom.sms.defaultServiceId"
+const char* kObservedPrefs[] = {
+  kPrefDefaultServiceId,
+  nullptr
+};
+
+uint32_t
+getDefaultServiceId()
+{
+  int32_t id = mozilla::Preferences::GetInt(kPrefDefaultServiceId, 0);
+  int32_t numRil = mozilla::Preferences::GetInt(kPrefRilNumRadioInterfaces, 1);
+
+  if (id >= numRil || id < 0) {
+    id = 0;
+  }
+
+  return id;
+}
+
+} // Anonymous namespace
+
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
-NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
+NS_IMPL_ISUPPORTS2(SmsService,
+                   nsISmsService,
+                   nsIObserver)
 
 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!");
+
+  // Initialize observer.
+  Preferences::AddStrongObservers(this, kObservedPrefs);
+  mDefaultServiceId = getDefaultServiceId();
+}
+
+/*
+ * Implementation of nsIObserver.
+ */
+
+NS_IMETHODIMP
+SmsService::Observe(nsISupports* aSubject,
+                    const char* aTopic,
+                    const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    nsDependentString data(aData);
+    if (data.EqualsLiteral(kPrefDefaultServiceId)) {
+      mDefaultServiceId = getDefaultServiceId();
+    }
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "SmsService got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+/*
+ * Implementation of nsISmsService.
+ */
+
+NS_IMETHODIMP
+SmsService::GetSmsDefaultServiceId(uint32_t* aServiceId)
+{
+  *aServiceId = mDefaultServiceId;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 SmsService::HasSupport(bool* aHasSupport)
 {
   *aHasSupport = true;
   return NS_OK;
 }
--- a/dom/mobilemessage/src/gonk/SmsService.h
+++ b/dom/mobilemessage/src/gonk/SmsService.h
@@ -2,35 +2,40 @@
  * 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 "nsIObserver.h"
 #include "nsIRadioInterfaceLayer.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 class SmsService MOZ_FINAL : public nsISmsService
+                           , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISMSSERVICE
+  NS_DECL_NSIOBSERVER
+
   SmsService();
 
 protected:
   // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS
   nsCOMPtr<nsIRadioInterface> mRadioInterface;
   nsTArray<nsString> mSilentNumbers;
+  uint32_t mDefaultServiceId;
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_SmsService_h
--- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
@@ -12,22 +12,33 @@
 #include "SmsFilter.h"
 #include "SmsSegmentInfo.h"
 #include "DictionaryHelpers.h"
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "mozilla/dom/MobileMessageManagerBinding.h"
 #include "mozilla/dom/MozMmsMessageBinding.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsString.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobilemessage;
 
 namespace {
 
+const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+#define kPrefMmsDefaultServiceId "dom.mms.defaultServiceId"
+#define kPrefSmsDefaultServiceId "dom.sms.defaultServiceId"
+const char* kObservedPrefs[] = {
+  kPrefMmsDefaultServiceId,
+  kPrefSmsDefaultServiceId,
+  nullptr
+};
+
 // TODO: Bug 767082 - WebSMS: sSmsChild leaks at shutdown
 PSmsChild* gSmsChild;
 
 PSmsChild*
 GetSmsChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -69,26 +80,79 @@ SendCursorRequest(const IPCMobileMessage
   // SmsChild::DeallocPMobileMessageCursor().
   actor->AddRef();
 
   smsChild->SendPMobileMessageCursorConstructor(actor, aRequest);
 
   actor.forget(aResult);
   return NS_OK;
 }
+
+uint32_t
+getDefaultServiceId(const char* aPrefKey)
+{
+  int32_t id = mozilla::Preferences::GetInt(aPrefKey, 0);
+  int32_t numRil = mozilla::Preferences::GetInt(kPrefRilNumRadioInterfaces, 1);
+
+  if (id >= numRil || id < 0) {
+    id = 0;
+  }
+
+  return id;
+}
+
 } // anonymous namespace
 
-NS_IMPL_ISUPPORTS3(SmsIPCService,
+NS_IMPL_ISUPPORTS4(SmsIPCService,
                    nsISmsService,
                    nsIMmsService,
-                   nsIMobileMessageDatabaseService)
+                   nsIMobileMessageDatabaseService,
+                   nsIObserver)
+
+SmsIPCService::SmsIPCService()
+{
+  Preferences::AddStrongObservers(this, kObservedPrefs);
+  mMmsDefaultServiceId = getDefaultServiceId(kPrefMmsDefaultServiceId);
+  mSmsDefaultServiceId = getDefaultServiceId(kPrefSmsDefaultServiceId);
+}
+
+/*
+ * Implementation of nsIObserver.
+ */
+
+NS_IMETHODIMP
+SmsIPCService::Observe(nsISupports* aSubject,
+                       const char* aTopic,
+                       const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    nsDependentString data(aData);
+    if (data.EqualsLiteral(kPrefMmsDefaultServiceId)) {
+      mMmsDefaultServiceId = getDefaultServiceId(kPrefMmsDefaultServiceId);
+    } else if (data.EqualsLiteral(kPrefSmsDefaultServiceId)) {
+      mSmsDefaultServiceId = getDefaultServiceId(kPrefSmsDefaultServiceId);
+    }
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "SmsIPCService got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
 
 /*
  * Implementation of nsISmsService.
  */
+
+NS_IMETHODIMP
+SmsIPCService::GetSmsDefaultServiceId(uint32_t* aServiceId)
+{
+  *aServiceId = mSmsDefaultServiceId;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 SmsIPCService::HasSupport(bool* aHasSupport)
 {
   PSmsChild* smsChild = GetSmsChild();
   NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE);
 
   smsChild->SendHasSupport(aHasSupport);
 
@@ -231,16 +295,27 @@ GetSendMmsMessageRequestFromParams(const
   }
 
   request.smil() = params.mSmil;
   request.subject() = params.mSubject;
 
   return true;
 }
 
+/*
+ * Implementation of nsIMmsService.
+ */
+
+NS_IMETHODIMP
+SmsIPCService::GetMmsDefaultServiceId(uint32_t* aServiceId)
+{
+  *aServiceId = mMmsDefaultServiceId;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 SmsIPCService::Send(const JS::Value& aParameters,
                     nsIMobileMessageCallback *aRequest)
 {
   SendMmsMessageRequest req;
   if (!GetSendMmsMessageRequestFromParams(aParameters, req)) {
     return NS_ERROR_INVALID_ARG;
   }
--- a/dom/mobilemessage/src/ipc/SmsIPCService.h
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.h
@@ -4,32 +4,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_mobilemessage_SmsIPCService_h
 #define mozilla_dom_mobilemessage_SmsIPCService_h
 
 #include "nsISmsService.h"
 #include "nsIMmsService.h"
 #include "nsIMobileMessageDatabaseService.h"
+#include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 class PSmsChild;
 
 class SmsIPCService MOZ_FINAL : public nsISmsService
                               , public nsIMmsService
                               , public nsIMobileMessageDatabaseService
+                              , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISMSSERVICE
   NS_DECL_NSIMMSSERVICE
   NS_DECL_NSIMOBILEMESSAGEDATABASESERVICE
+  NS_DECL_NSIOBSERVER
+
+  SmsIPCService();
+
+private:
+  uint32_t mMmsDefaultServiceId;
+  uint32_t mSmsDefaultServiceId;
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_SmsIPCService_h
--- a/dom/mobilemessage/tests/marionette/manifest.ini
+++ b/dom/mobilemessage/tests/marionette/manifest.ini
@@ -31,8 +31,9 @@ qemu = true
 [test_outgoing_max_segments.js]
 [test_update_thread_record_in_delete.js]
 [test_massive_incoming_delete.js]
 [test_getsegmentinfofortext.js]
 [test_phone_number_normalization.js]
 [test_invalid_address.js]
 [test_mmsmessage_attachments.js]
 [test_getthreads.js]
+[test_dsds_default_service_id.js]
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/tests/marionette/test_dsds_default_service_id.js
@@ -0,0 +1,130 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+
+const MMS_SERVICE_CONTRACTID = "@mozilla.org/mms/mmsservice;1";
+const SMS_SERVICE_CONTRACTID = "@mozilla.org/sms/smsservice;1";
+
+const PREF_RIL_NUM_RADIO_INTERFACES = "ril.numRadioInterfaces";
+const PREF_MMS_DEFAULT_SERVICE_ID = "dom.mms.defaultServiceId";
+const PREF_SMS_DEFAULT_SERVICE_ID = "dom.sms.defaultServiceId";
+
+function setPrefAndVerify(prefKey, setVal, service, attrName, expectedVal, deferred) {
+  log("  Set '" + prefKey + "' to " + setVal);
+  Services.prefs.setIntPref(prefKey, setVal);
+  let prefVal = Services.prefs.getIntPref(prefKey);
+  is(prefVal, setVal, "'" + prefKey + "' set to " + setVal);
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, expectedVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+}
+
+function getNumRadioInterfaces() {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    let numRil = Services.prefs.getIntPref(PREF_RIL_NUM_RADIO_INTERFACES);
+    log("numRil = " + numRil);
+
+    deferred.resolve(numRil);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function getService(contractId, ifaceName) {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    log("Getting service for " + ifaceName);
+    let service = Cc[contractId].getService(Ci[ifaceName]);
+    ok(service, "service.constructor is " + service.constructor);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkInitialEquality(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  log("  Checking initial value for '" + prefKey + "'");
+  let origPrefVal = Services.prefs.getIntPref(prefKey);
+  ok(isFinite(origPrefVal), "default '" + prefKey + "' value");
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, origPrefVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkSetToNegtiveValue(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  // Set to -1 and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, -1, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkSetToOverflowedValue(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  // Set to larger-equal than numRil and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, numRil, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkValueChange(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  if (numRil > 1) {
+    // Set to (numRil - 1) and verify defaultVal equals.
+    setPrefAndVerify(prefKey, numRil - 1, service, attrName, numRil - 1, deferred);
+  } else {
+    window.setTimeout(function () {
+      deferred.resolve(service);
+    }, 0);
+  }
+
+  return deferred.promise;
+}
+
+function verify(contractId, ifaceName, attrName, prefKey, numRil) {
+  let deferred = Promise.defer();
+
+  getService(contractId, ifaceName)
+    .then(checkInitialEquality.bind(null, attrName, prefKey))
+    .then(checkSetToNegtiveValue.bind(null, attrName, prefKey))
+    .then(checkSetToOverflowedValue.bind(null, attrName, prefKey, numRil))
+    .then(checkValueChange.bind(null, attrName, prefKey, numRil))
+    .then(function () {
+      // Reset.
+      Services.prefs.clearUserPref(prefKey);
+
+      deferred.resolve(numRil);
+    });
+
+  return deferred.promise;
+}
+
+getNumRadioInterfaces()
+  .then(verify.bind(null, MMS_SERVICE_CONTRACTID, "nsIMmsService",
+                    "mmsDefaultServiceId", PREF_MMS_DEFAULT_SERVICE_ID))
+  .then(verify.bind(null, SMS_SERVICE_CONTRACTID, "nsISmsService",
+                    "smsDefaultServiceId", PREF_SMS_DEFAULT_SERVICE_ID))
+  .then(finish);
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -20,32 +20,27 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
-// set to true to in ril_consts.js to see debug messages
-var DEBUG = RIL.DEBUG_CONTENT_HELPER;
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
 
-// Read debug setting from pref
-try {
-  let debugPref = Services.prefs.getBoolPref("ril.debugging.enabled");
-  DEBUG = RIL.DEBUG_CONTENT_HELPER || debugPref;
-} catch (e) {}
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
+const kPrefVoicemailDefaultServiceId = "dom.voicemail.defaultServiceId";
 
-let debug;
-if (DEBUG) {
-  debug = function (s) {
-    dump("-*- RILContentHelper: " + s + "\n");
-  };
-} else {
-  debug = function (s) {};
+let DEBUG;
+function debug(s) {
+  dump("-*- RILContentHelper: " + s + "\n");
 }
 
 const RILCONTENTHELPER_CID =
   Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
 const GSMICCINFO_CID =
   Components.ID("{e0fa785b-ad3f-46ed-bc56-fcb0d6fe4fa8}");
 const CDMAICCINFO_CID =
   Components.ID("{3d1f844f-9ec5-48fb-8907-aed2e5421709}");
@@ -121,18 +116,17 @@ XPCOMUtils.defineLazyGetter(this, "gNumR
   let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
                           .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 
   if (isParentProcess) {
     let ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
     return ril.numRadioInterfaces;
   }
 
-  let num = cpmm.sendSyncMessage("RIL:GetNumRadioInterfaces")[0];
-  return num;
+  return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
 });
 
 function MobileIccCardLockResult(options) {
   this.lockType = options.lockType;
   this.enabled = options.enabled;
   this.retryCount = options.retryCount;
   this.success = options.success;
 }
@@ -444,28 +438,35 @@ IccCardLockError.prototype = {
   __init: function(lockType, errorMsg, retryCount) {
     this.__DOM_IMPL__.init(errorMsg);
     this.lockType = lockType;
     this.retryCount = retryCount;
   },
 };
 
 function RILContentHelper() {
+  this.updateDebugFlag();
+
   this.rilContext = {
     cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
     networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
     iccInfo:              null,
     voiceConnectionInfo:  new MobileConnectionInfo(),
     dataConnectionInfo:   new MobileConnectionInfo()
   };
   this.voicemailInfo = new VoicemailInfo();
+  this.voicemailDefaultServiceId = this.getVoicemailDefaultServiceId();
 
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
-  Services.obs.addObserver(this, "xpcom-shutdown", false);
+
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+
+  Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
+  Services.prefs.addObserver(kPrefVoicemailDefaultServiceId, this, false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider,
                                          Ci.nsICellBroadcastProvider,
                                          Ci.nsIVoicemailProvider,
@@ -475,16 +476,23 @@ RILContentHelper.prototype = {
   classID:   RILCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
                                     classDescription: "RILContentHelper",
                                     interfaces: [Ci.nsIMobileConnectionProvider,
                                                  Ci.nsICellBroadcastProvider,
                                                  Ci.nsIVoicemailProvider,
                                                  Ci.nsIIccProvider]}),
 
+  updateDebugFlag: function updateDebugFlag() {
+    try {
+      DEBUG = RIL.DEBUG_CONTENT_HELPER ||
+              Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
+    } catch (e) {}
+  },
+
   // An utility function to copy objects.
   updateInfo: function updateInfo(srcInfo, destInfo) {
     for (let key in srcInfo) {
       destInfo[key] = srcInfo[key];
     }
   },
 
   updateConnectionInfo: function updateConnectionInfo(srcInfo, destInfo) {
@@ -1308,33 +1316,46 @@ RILContentHelper.prototype = {
 
   _mobileConnectionListeners: null,
   _cellBroadcastListeners: null,
   _voicemailListeners: null,
   _iccListeners: null,
 
   voicemailStatus: null,
 
+  voicemailDefaultServiceId: 0,
+  getVoicemailDefaultServiceId: function getVoicemailDefaultServiceId() {
+    let id = Services.prefs.getIntPref(kPrefVoicemailDefaultServiceId);
+
+    if (id >= gNumRadioInterfaces || id < 0) {
+      id = 0;
+    }
+
+    return id;
+  },
+
   getVoicemailInfo: function getVoicemailInfo() {
     // Get voicemail infomation by IPC only on first time.
     this.getVoicemailInfo = function getVoicemailInfo() {
       return this.voicemailInfo;
     };
 
     let voicemailInfo =
       cpmm.sendSyncMessage("RIL:GetVoicemailInfo", {clientId: 0})[0];
     if (voicemailInfo) {
       this.updateInfo(voicemailInfo, this.voicemailInfo);
     }
 
     return this.voicemailInfo;
   },
+
   get voicemailNumber() {
     return this.getVoicemailInfo().number;
   },
+
   get voicemailDisplayName() {
     return this.getVoicemailInfo().displayName;
   },
 
   registerListener: function registerListener(listenerType, listener) {
     let listeners = this[listenerType];
     if (!listeners) {
       listeners = this[listenerType] = [];
@@ -1399,19 +1420,29 @@ RILContentHelper.prototype = {
 
   unregisterIccMsg: function unregisterIccMsg(listener) {
     this.unregisterListener("_iccListeners", listener);
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
-    if (topic == "xpcom-shutdown") {
-      this.destroyDOMRequestHelper();
-      Services.obs.removeObserver(this, "xpcom-shutdown");
+    switch (topic) {
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (data == kPrefRilDebuggingEnabled) {
+          this.updateDebugFlag();
+        } else if (data == kPrefVoicemailDefaultServiceId) {
+          this.voicemailDefaultServiceId = this.getVoicemailDefaultServiceId();
+        }
+        break;
+
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
+        this.destroyDOMRequestHelper();
+        Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+        break;
     }
   },
 
   // nsIMessageListener
 
   fireRequestSuccess: function fireRequestSuccess(requestId, result) {
     let request = this.takeRequest(requestId);
     if (!request) {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -47,47 +47,51 @@ const RADIOINTERFACE_CID =
   Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
 const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 const GSMICCINFO_CID =
   Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
 const CDMAICCINFO_CID =
   Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
 
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 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 kClockAutoUpdateEnabled            = "time.clock.automatic-update.enabled";
-const kClockAutoUpdateAvailable          = "time.clock.automatic-update.available";
-const kTimezoneAutoUpdateEnabled         = "time.timezone.automatic-update.enabled";
-const kTimezoneAutoUpdateAvailable       = "time.timezone.automatic-update.available";
-const kCellBroadcastSearchList           = "ril.cellbroadcast.searchlist";
-const kCellBroadcastDisabled             = "ril.cellbroadcast.disabled";
-const kPrefenceChangedObserverTopic      = "nsPref:changed";
-const kClirModePreference                = "ril.clirMode";
+
+const kSettingsCellBroadcastSearchList = "ril.cellbroadcast.searchlist";
+const kSettingsClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
+const kSettingsClockAutoUpdateAvailable = "time.clock.automatic-update.available";
+const kSettingsTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
+const kSettingsTimezoneAutoUpdateAvailable = "time.timezone.automatic-update.available";
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+const kPrefCellBroadcastDisabled = "ril.cellbroadcast.disabled";
+const kPrefClirModePreference = "ril.clirMode";
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
 
 const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
 const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
 
 const RADIO_POWER_OFF_TIMEOUT = 30000;
 const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
 
 const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
-  "RIL:GetNumRadioInterfaces",
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:RegisterMobileConnectionMsg",
   "RIL:SetCallForwardingOption",
@@ -102,17 +106,16 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES
   "RIL:SetRoamingPreference",
   "RIL:GetRoamingPreference",
   "RIL:ExitEmergencyCbMode",
   "RIL:SetVoicePrivacyMode",
   "RIL:GetVoicePrivacyMode"
 ];
 
 const RIL_IPC_ICCMANAGER_MSG_NAMES = [
-  "RIL:GetNumRadioInterfaces",
   "RIL:SendStkResponse",
   "RIL:SendStkMenuSelection",
   "RIL:SendStkTimerExpiration",
   "RIL:SendStkEventDownload",
   "RIL:GetCardLockState",
   "RIL:UnlockCardLock",
   "RIL:SetCardLock",
   "RIL:GetCardLockRetryCount",
@@ -120,23 +123,21 @@ const RIL_IPC_ICCMANAGER_MSG_NAMES = [
   "RIL:IccExchangeAPDU",
   "RIL:IccCloseChannel",
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact",
   "RIL:RegisterIccMsg"
 ];
 
 const RIL_IPC_VOICEMAIL_MSG_NAMES = [
-  "RIL:GetNumRadioInterfaces",
   "RIL:RegisterVoicemailMsg",
   "RIL:GetVoicemailInfo"
 ];
 
 const RIL_IPC_CELLBROADCAST_MSG_NAMES = [
-  "RIL:GetNumRadioInterfaces",
   "RIL:RegisterCellBroadcastMsg"
 ];
 
 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
                                    "@mozilla.org/power/powermanagerservice;1",
                                    "nsIPowerManagerService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
@@ -204,25 +205,25 @@ XPCOMUtils.defineLazyGetter(this, "gMess
     topics: [],
 
     targetMessageQueue: [],
     ready: false,
 
     init: function init(ril) {
       this.ril = ril;
 
-      Services.obs.addObserver(this, "xpcom-shutdown", false);
+      Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
       Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
       this._registerMessageListeners();
     },
 
     _shutdown: function _shutdown() {
       this.ril = null;
 
-      Services.obs.removeObserver(this, "xpcom-shutdown");
+      Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
       this._unregisterMessageListeners();
     },
 
     _registerMessageListeners: function _registerMessageListeners() {
       ppmm.addMessageListener("child-process-shutdown", this);
       for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
         ppmm.addMessageListener(msgname, this);
       }
@@ -390,18 +391,16 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           return null;
         }
       } else {
         if (DEBUG) debug("Ignoring unknown message type: " + msg.name);
         return null;
       }
 
       switch (msg.name) {
-        case "RIL:GetNumRadioInterfaces":
-          return this.ril.numRadioInterfaces;
         case "RIL:RegisterMobileConnectionMsg":
           this._registerMessageTarget("mobileconnection", msg.target);
           return null;
         case "RIL:RegisterIccMsg":
           this._registerMessageTarget("icc", msg.target);
           return null;
         case "RIL:RegisterVoicemailMsg":
           this._registerMessageTarget("voicemail", msg.target);
@@ -426,17 +425,17 @@ XPCOMUtils.defineLazyGetter(this, "gMess
      */
 
     observe: function observe(subject, topic, data) {
       switch (topic) {
         case kSysMsgListenerReadyObserverTopic:
           Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
           this._resendQueuedTargetMessage();
           break;
-        case "xpcom-shutdown":
+        case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
           this._shutdown();
           break;
       }
     },
 
     sendMobileConnectionMessage: function sendMobileConnectionMessage(message, clientId, data) {
       this._sendTargetMessage("mobileconnection", message, {
         clientId: clientId,
@@ -462,16 +461,35 @@ XPCOMUtils.defineLazyGetter(this, "gMess
       this._sendTargetMessage("icc", message, {
         clientId: clientId,
         data: data
       });
     }
   };
 });
 
+// Initialize shared preference 'ril.numRadioInterfaces' according to system
+// property.
+try {
+  Services.prefs.setIntPref(kPrefRilNumRadioInterfaces, (function () {
+    // When Gonk property "ro.moz.ril.numclients" is not set, return 1; if
+    // explicitly set to any number larger-equal than 0, return num; else, return
+    // 1 for compatibility.
+    try {
+      let numString = libcutils.property_get("ro.moz.ril.numclients", "1");
+      let num = parseInt(numString, 10);
+      if (num >= 0) {
+        return num;
+      }
+    } catch (e) {}
+
+    return 1;
+  })());
+} catch (e) {}
+
 function IccInfo() {}
 IccInfo.prototype = {
   iccType: null,
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
   isDisplayNetworkNameRequired: null,
@@ -519,21 +537,21 @@ function RadioInterfaceLayer() {
   let options = {
     debug: debugPref,
     cellBroadcastDisabled: false,
     clirMode: RIL.CLIR_DEFAULT
   };
 
   try {
     options.cellBroadcastDisabled =
-      Services.prefs.getBoolPref(kCellBroadcastDisabled);
+      Services.prefs.getBoolPref(kPrefCellBroadcastDisabled);
   } catch(e) {}
 
   try {
-    options.clirMode = Services.prefs.getIntPref(kClirModePreference);
+    options.clirMode = Services.prefs.getIntPref(kPrefClirModePreference);
   } catch(e) {}
 
   let numIfaces = this.numRadioInterfaces;
   debug(numIfaces + " interfaces");
   this.radioInterfaces = [];
   for (let clientId = 0; clientId < numIfaces; clientId++) {
     options.clientId = clientId;
     this.radioInterfaces.push(new RadioInterface(options));
@@ -563,26 +581,19 @@ RadioInterfaceLayer.prototype = {
 
   getRadioInterface: function getRadioInterface(clientId) {
     return this.radioInterfaces[clientId];
   }
 };
 
 XPCOMUtils.defineLazyGetter(RadioInterfaceLayer.prototype,
                             "numRadioInterfaces", function () {
-  // When Gonk property "ro.moz.ril.numclients" is not set, return 1; if
-  // explicitly set to any number larger-equal than 0, return num; else, return
-  // 1 for compatibility.
   try {
-    let numString = libcutils.property_get("ro.moz.ril.numclients", "1");
-    let num = parseInt(numString, 10);
-    if (num >= 0) {
-      return num;
-    }
-  } catch (e) {}
+    return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
+  } catch(e) {}
 
   return 1;
 });
 
 function WorkerMessenger(radioInterface, options) {
   // Initial owning attributes.
   this.radioInterface = radioInterface;
   this.tokenCallbackMap = {};
@@ -776,40 +787,40 @@ function RadioInterface(options) {
 
   // Read the APN data from the settings DB.
   lock.get("ril.data.roaming_enabled", this);
   lock.get("ril.data.enabled", this);
   lock.get("ril.data.apnSettings", this);
 
   // Read the 'time.clock.automatic-update.enabled' setting to see if
   // we need to adjust the system clock time by NITZ or SNTP.
-  lock.get(kClockAutoUpdateEnabled, this);
+  lock.get(kSettingsClockAutoUpdateEnabled, this);
 
   // Read the 'time.timezone.automatic-update.enabled' setting to see if
   // we need to adjust the system timezone by NITZ.
-  lock.get(kTimezoneAutoUpdateEnabled, this);
+  lock.get(kSettingsTimezoneAutoUpdateEnabled, this);
 
   // Set "time.clock.automatic-update.available" to false when starting up.
   this.setClockAutoUpdateAvailable(false);
 
   // Set "time.timezone.automatic-update.available" to false when starting up.
   this.setTimezoneAutoUpdateAvailable(false);
 
   // Read the Cell Broadcast Search List setting, string of integers or integer
   // ranges separated by comma, to set listening channels.
-  lock.get(kCellBroadcastSearchList, this);
-
-  Services.obs.addObserver(this, "xpcom-shutdown", false);
+  lock.get(kSettingsCellBroadcastSearchList, this);
+
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, 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.obs.addObserver(this, kNetworkInterfaceStateChangedTopic, false);
-  Services.prefs.addObserver(kCellBroadcastDisabled, this, false);
+  Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false);
 
   this.portAddressedSmsApps = {};
   this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
 
   this._sntp = new Sntp(this.setClockBySntp.bind(this),
                         Services.prefs.getIntPref('network.sntp.maxRetryCount'),
                         Services.prefs.getIntPref('network.sntp.refreshPeriod'),
                         Services.prefs.getIntPref('network.sntp.timeout'),
@@ -1331,17 +1342,17 @@ RadioInterface.prototype = {
       return;
     }
 
     this.workerMessenger.send("setCellBroadcastSearchList",
                               { searchListStr: newSearchListStr },
                               (function callback(response) {
       if (!response.success) {
         let lock = gSettingsService.createLock();
-        lock.set(kCellBroadcastSearchList,
+        lock.set(kSettingsCellBroadcastSearchList,
                  this._cellBroadcastSearchListStr, null);
       } else {
         this._cellBroadcastSearchListStr = response.searchListStr;
       }
 
       return false;
     }).bind(this));
   },
@@ -1963,25 +1974,25 @@ RadioInterface.prototype = {
     this._deliverDataCallCallback("receiveDataCallList",
                                   [message.datacalls, message.datacalls.length]);
   },
 
   /**
    * Set the setting value of "time.clock.automatic-update.available".
    */
   setClockAutoUpdateAvailable: function setClockAutoUpdateAvailable(value) {
-    gSettingsService.createLock().set(kClockAutoUpdateAvailable, value, null,
+    gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null,
                                       "fromInternalSetting");
   },
 
   /**
    * Set the setting value of "time.timezone.automatic-update.available".
    */
   setTimezoneAutoUpdateAvailable: function setTimezoneAutoUpdateAvailable(value) {
-    gSettingsService.createLock().set(kTimezoneAutoUpdateAvailable, value, null,
+    gSettingsService.createLock().set(kSettingsTimezoneAutoUpdateAvailable, value, null,
                                       "fromInternalSetting");
   },
 
   /**
    * Set the system clock by NITZ.
    */
   setClockByNitz: function setClockByNitz(message) {
     // To set the system clock time. Note that there could be a time diff
@@ -2147,42 +2158,41 @@ RadioInterface.prototype = {
         Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
         this._sysMsgListenerReady = true;
         this._ensureRadioState();
         break;
       case kMozSettingsChangedObserverTopic:
         let setting = JSON.parse(data);
         this.handleSettingsChange(setting.key, setting.value, setting.message);
         break;
-      case kPrefenceChangedObserverTopic:
-        if (data === kCellBroadcastDisabled) {
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (data === kPrefCellBroadcastDisabled) {
           let value = false;
           try {
-            value = Services.prefs.getBoolPref(kCellBroadcastDisabled);
+            value = Services.prefs.getBoolPref(kPrefCellBroadcastDisabled);
           } catch(e) {}
           this.workerMessenger.send("setCellBroadcastDisabled",
                                     { disabled: value });
         }
         break;
-      case "xpcom-shutdown":
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
         // Cancel the timer of the CPU wake lock for handling the received SMS.
         this._cancelSmsHandledWakeLockTimer();
 
         // Shutdown all RIL network interfaces
         for each (let apnSetting in this.apnSettings.byAPN) {
           if (apnSetting.iface) {
             apnSetting.iface.shutdown();
           }
         }
-        Services.obs.removeObserver(this, "xpcom-shutdown");
+        Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
         Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
         Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
         Services.obs.removeObserver(this, kScreenStateChangedTopic);
         Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
-        Services.prefs.removeObserver(kCellBroadcastDisabled, this);
         break;
       case kSysClockChangeObserverTopic:
         let offset = parseInt(data, 10);
         if (this._lastNitzMessage) {
           this._lastNitzMessage.receiveTimeInMS += offset;
         }
         this._sntp.updateOffset(offset);
         break;
@@ -2235,31 +2245,31 @@ RadioInterface.prototype = {
   _sntp: null,
 
   // Cell Broadcast settings values.
   _cellBroadcastSearchListStr: null,
 
   handleSettingsChange: function handleSettingsChange(aName, aResult, aMessage) {
     // Don't allow any content processes to modify the setting
     // "time.clock.automatic-update.available" except for the chrome process.
-    if (aName === kClockAutoUpdateAvailable &&
+    if (aName === kSettingsClockAutoUpdateAvailable &&
         aMessage !== "fromInternalSetting") {
       let isClockAutoUpdateAvailable = this._lastNitzMessage !== null ||
                                        this._sntp.isAvailable();
       if (aResult !== isClockAutoUpdateAvailable) {
         debug("Content processes cannot modify 'time.clock.automatic-update.available'. Restore!");
         // Restore the setting to the current value.
         this.setClockAutoUpdateAvailable(isClockAutoUpdateAvailable);
       }
     }
 
     // Don't allow any content processes to modify the setting
     // "time.timezone.automatic-update.available" except for the chrome
     // process.
-    if (aName === kTimezoneAutoUpdateAvailable &&
+    if (aName === kSettingsTimezoneAutoUpdateAvailable &&
         aMessage !== "fromInternalSetting") {
       let isTimezoneAutoUpdateAvailable = this._lastNitzMessage !== null;
       if (aResult !== isTimezoneAutoUpdateAvailable) {
         if (DEBUG) {
           this.debug("Content processes cannot modify 'time.timezone.automatic-update.available'. Restore!");
         }
         // Restore the setting to the current value.
         this.setTimezoneAutoUpdateAvailable(isTimezoneAutoUpdateAvailable);
@@ -2301,17 +2311,17 @@ RadioInterface.prototype = {
         break;
       case "ril.data.apnSettings":
         if (DEBUG) this.debug("'ril.data.apnSettings' is now " + JSON.stringify(aResult));
         if (aResult) {
           this.updateApnSettings(aResult);
           this.updateRILNetworkInterface();
         }
         break;
-      case kClockAutoUpdateEnabled:
+      case kSettingsClockAutoUpdateEnabled:
         this._clockAutoUpdateEnabled = aResult;
         if (!this._clockAutoUpdateEnabled) {
           break;
         }
 
         // Set the latest cached NITZ time if it's available.
         if (this._lastNitzMessage) {
           this.setClockByNitz(this._lastNitzMessage);
@@ -2321,29 +2331,29 @@ RadioInterface.prototype = {
           if (!this._sntp.isExpired()) {
             this.setClockBySntp(this._sntp.getOffset());
           } else {
             // Or refresh the SNTP.
             this._sntp.request();
           }
         }
         break;
-      case kTimezoneAutoUpdateEnabled:
+      case kSettingsTimezoneAutoUpdateEnabled:
         this._timezoneAutoUpdateEnabled = aResult;
 
         if (this._timezoneAutoUpdateEnabled) {
           // Apply the latest cached NITZ for timezone if it's available.
           if (this._timezoneAutoUpdateEnabled && this._lastNitzMessage) {
             this.setTimezoneByNitz(this._lastNitzMessage);
           }
         }
         break;
-      case kCellBroadcastSearchList:
+      case kSettingsCellBroadcastSearchList:
         if (DEBUG) {
-          this.debug("'" + kCellBroadcastSearchList + "' is now " + aResult);
+          this.debug("'" + kSettingsCellBroadcastSearchList + "' is now " + aResult);
         }
         this.setCellBroadcastSearchList(aResult);
         break;
     }
   },
 
   handleError: function handleError(aErrorMessage) {
     if (DEBUG) this.debug("There was an error while reading RIL settings.");
@@ -2376,20 +2386,20 @@ RadioInterface.prototype = {
   _sendCfStateChanged: function _sendCfStateChanged(message) {
     gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged",
                                                 this.clientId, message);
   },
 
   _updateCallingLineIdRestrictionPref:
     function _updateCallingLineIdRestrictionPref(mode) {
     try {
-      Services.prefs.setIntPref(kClirModePreference, mode);
+      Services.prefs.setIntPref(kPrefClirModePreference, mode);
       Services.prefs.savePrefFile(null);
       if (DEBUG) {
-        this.debug(kClirModePreference + " pref is now " + mode);
+        this.debug(kPrefClirModePreference + " pref is now " + mode);
       }
     } catch (e) {}
   },
 
   sendMMI: function sendMMI(target, message) {
     if (DEBUG) this.debug("SendMMI " + JSON.stringify(message));
     this.workerMessenger.send("sendMMI", message, (function(response) {
       if (response.isSetCallForward) {
--- a/dom/system/gonk/tests/marionette/manifest.ini
+++ b/dom/system/gonk/tests/marionette/manifest.ini
@@ -3,8 +3,9 @@ b2g = true
 browser = false
 qemu = true
 
 [test_geolocation.js]
 disabled = Bug 808783
 [test_fakevolume.js]
 [test_ril_code_quality.py]
 [test_screen_state.js]
+[test_dsds_numRadioInterfaces.js]
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_dsds_numRadioInterfaces.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+
+const NS_RIL_CONTRACTID = "@mozilla.org/ril;1";
+
+const PROP_RO_MOZ_RIL_NUMCLIENTS = "ro.moz.ril.numclients";
+
+const PREF_RIL_NUM_RADIO_INTERFACES = "ril.numRadioInterfaces";
+
+ok(libcutils, "libcutils is available");
+
+let propNum = (function () {
+  try {
+    let numString = libcutils.property_get(PROP_RO_MOZ_RIL_NUMCLIENTS, "1");
+    let num = parseInt(numString, 10);
+    if (num >= 0) {
+      return num;
+    }
+  } catch (e) {}
+})();
+
+log("Retrieved '" + PROP_RO_MOZ_RIL_NUMCLIENTS + "' = " + propNum);
+ok(propNum, PROP_RO_MOZ_RIL_NUMCLIENTS);
+
+let prefNum = Services.prefs.getIntPref(PREF_RIL_NUM_RADIO_INTERFACES);
+log("Retrieved '" + PREF_RIL_NUM_RADIO_INTERFACES + "' = " + prefNum);
+
+let ril = Cc[NS_RIL_CONTRACTID].getService(Ci.nsIRadioInterfaceLayer);
+ok(ril, "ril.constructor is " + ril.constructor);
+
+let ifaceNum = ril.numRadioInterfaces;
+log("Retrieved 'nsIRadioInterfaceLayer.numRadioInterfaces' = " + ifaceNum);
+
+is(propNum, prefNum);
+is(propNum, ifaceNum);
+
+finish();
--- a/dom/telephony/gonk/TelephonyProvider.js
+++ b/dom/telephony/gonk/TelephonyProvider.js
@@ -13,18 +13,23 @@ Cu.import("resource://gre/modules/Servic
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
 const GONK_TELEPHONYPROVIDER_CONTRACTID =
   "@mozilla.org/telephony/gonktelephonyprovider;1";
 const GONK_TELEPHONYPROVIDER_CID =
   Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}");
 
-const kPrefenceChangedObserverTopic = "nsPref:changed";
-const kXpcomShutdownObserverTopic   = "xpcom-shutdown";
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID   = "xpcom-shutdown";
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
+const kPrefDefaultServiceId = "dom.telephony.defaultServiceId";
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsITelephonyProvider = Ci.nsITelephonyProvider;
 
 const CALL_WAKELOCK_TIMEOUT = 5000;
 
 let DEBUG;
 function debug(s) {
@@ -78,19 +83,22 @@ XPCOMUtils.defineLazyGetter(this, "gPhon
   Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
   return ns.PhoneNumberUtils;
 });
 
 function TelephonyProvider() {
   this._listeners = [];
 
   this._updateDebugFlag();
+  this.defaultServiceId = this._getDefaultServiceId();
 
-  Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false);
-  Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
+  Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
+  Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
+
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 }
 TelephonyProvider.prototype = {
   classID: GONK_TELEPHONYPROVIDER_CID,
   classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYPROVIDER_CID,
                                     contractID: GONK_TELEPHONYPROVIDER_CONTRACTID,
                                     classDescription: "TelephonyProvider",
                                     interfaces: [Ci.nsITelephonyProvider,
                                                  Ci.nsIGonkTelephonyProvider],
@@ -260,24 +268,37 @@ TelephonyProvider.prototype = {
     }
 
     return false;
   },
 
   _updateDebugFlag: function _updateDebugFlag() {
     try {
       DEBUG = RIL.DEBUG_RIL ||
-              Services.prefs.getBoolPref("ril.debugging.enabled");
+              Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
     } catch (e) {}
   },
 
+  _getDefaultServiceId: function _getDefaultServiceId() {
+    let id = Services.prefs.getIntPref(kPrefDefaultServiceId);
+    let numRil = Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
+
+    if (id >= numRil || id < 0) {
+      id = 0;
+    }
+
+    return id;
+  },
+
   /**
    * nsITelephonyProvider interface.
    */
 
+  defaultServiceId: 0,
+
   registerListener: function(aListener) {
     if (this._listeners.indexOf(aListener) >= 0) {
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
     this._listeners.push(aListener);
   },
 
@@ -501,26 +522,27 @@ TelephonyProvider.prototype = {
   },
 
   /**
    * nsIObserver interface.
    */
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case kPrefenceChangedObserverTopic:
-        if (aData === "ril.debugging.enabled") {
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (aData === kPrefRilDebuggingEnabled) {
           this._updateDebugFlag();
+	} else if (aData === kPrefDefaultServiceId) {
+          this.defaultServiceId = this._getDefaultServiceId();
         }
         break;
 
-      case kXpcomShutdownObserverTopic:
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
         // Cancel the timer for the call-ring wake lock.
         this._cancelCallRingWakeLockTimer();
 
-        Services.obs.removeObserver(this, kPrefenceChangedObserverTopic);
-        Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
+        Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
         break;
     }
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyProvider]);
--- a/dom/telephony/ipc/TelephonyIPCProvider.cpp
+++ b/dom/telephony/ipc/TelephonyIPCProvider.cpp
@@ -1,42 +1,100 @@
 /* -*- 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 "ipc/TelephonyIPCProvider.h"
+
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/telephony/TelephonyChild.h"
-#include "ipc/TelephonyIPCProvider.h"
+#include "mozilla/Preferences.h"
 
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom;
 
-NS_IMPL_ISUPPORTS2(TelephonyIPCProvider,
+namespace {
+
+const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+#define kPrefDefaultServiceId "dom.telephony.defaultServiceId"
+const char* kObservedPrefs[] = {
+  kPrefDefaultServiceId,
+  nullptr
+};
+
+uint32_t
+getDefaultServiceId()
+{
+  int32_t id = mozilla::Preferences::GetInt(kPrefDefaultServiceId, 0);
+  int32_t numRil = mozilla::Preferences::GetInt(kPrefRilNumRadioInterfaces, 1);
+
+  if (id >= numRil || id < 0) {
+    id = 0;
+  }
+
+  return id;
+}
+
+} // Anonymous namespace
+
+NS_IMPL_ISUPPORTS3(TelephonyIPCProvider,
                    nsITelephonyProvider,
-                   nsITelephonyListener)
+                   nsITelephonyListener,
+                   nsIObserver)
 
 TelephonyIPCProvider::TelephonyIPCProvider()
 {
   // Deallocated in ContentChild::DeallocPTelephonyChild().
   mPTelephonyChild = new TelephonyChild(this);
   ContentChild::GetSingleton()->SendPTelephonyConstructor(mPTelephonyChild);
+
+  Preferences::AddStrongObservers(this, kObservedPrefs);
+  mDefaultServiceId = getDefaultServiceId();
 }
 
 TelephonyIPCProvider::~TelephonyIPCProvider()
 {
   mPTelephonyChild->Send__delete__(mPTelephonyChild);
   mPTelephonyChild = nullptr;
 }
 
 /*
+ * Implementation of nsIObserver.
+ */
+
+NS_IMETHODIMP
+TelephonyIPCProvider::Observe(nsISupports* aSubject,
+                              const char* aTopic,
+                              const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    nsDependentString data(aData);
+    if (data.EqualsLiteral(kPrefDefaultServiceId)) {
+      mDefaultServiceId = getDefaultServiceId();
+    }
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "TelephonyIPCProvider got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+/*
  * Implementation of nsITelephonyProvider.
  */
 
 NS_IMETHODIMP
+TelephonyIPCProvider::GetDefaultServiceId(uint32_t* aServiceId)
+{
+  *aServiceId = mDefaultServiceId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TelephonyIPCProvider::RegisterListener(nsITelephonyListener *aListener)
 {
   MOZ_ASSERT(!mListeners.Contains(aListener));
 
   // nsTArray doesn't fail.
   mListeners.AppendElement(aListener);
 
   if (mListeners.Length() == 1) {
--- a/dom/telephony/ipc/TelephonyIPCProvider.h
+++ b/dom/telephony/ipc/TelephonyIPCProvider.h
@@ -3,35 +3,39 @@
  * 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_telephony_TelephonyIPCProvider_h
 #define mozilla_dom_telephony_TelephonyIPCProvider_h
 
 #include "mozilla/dom/telephony/TelephonyCommon.h"
 #include "mozilla/Attributes.h"
+#include "nsIObserver.h"
 #include "nsITelephonyProvider.h"
 
 BEGIN_TELEPHONY_NAMESPACE
 
 class PTelephonyChild;
 
 class TelephonyIPCProvider MOZ_FINAL : public nsITelephonyProvider
                                      , public nsITelephonyListener
+                                     , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYPROVIDER
   NS_DECL_NSITELEPHONYLISTENER
+  NS_DECL_NSIOBSERVER
 
   TelephonyIPCProvider();
 
 protected:
   virtual ~TelephonyIPCProvider();
 
 private:
   nsTArray<nsCOMPtr<nsITelephonyListener> > mListeners;
   PTelephonyChild* mPTelephonyChild;
+  uint32_t mDefaultServiceId;
 };
 
 END_TELEPHONY_NAMESPACE
 
 #endif // mozilla_dom_telephony_TelephonyIPCProvider_h
--- a/dom/telephony/nsITelephonyProvider.idl
+++ b/dom/telephony/nsITelephonyProvider.idl
@@ -113,17 +113,17 @@ interface nsITelephonyListener : nsISupp
 #define TELEPHONY_PROVIDER_CONTRACTID \
   "@mozilla.org/telephony/telephonyprovider;1"
 %}
 
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(effca006-1ca8-47f7-9bab-1323f90a14ec)]
+[scriptable, uuid(f7680b82-53fc-42a7-9adf-bc0f2726425c)]
 interface nsITelephonyProvider : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_CONNECTING = 3;
   const unsigned short CALL_STATE_CONNECTED = 4;
   const unsigned short CALL_STATE_HOLDING = 5;
@@ -131,16 +131,18 @@ interface nsITelephonyProvider : nsISupp
   const unsigned short CALL_STATE_RESUMING = 7;
   const unsigned short CALL_STATE_DISCONNECTING = 8;
   const unsigned short CALL_STATE_DISCONNECTED = 9;
   const unsigned short CALL_STATE_INCOMING = 10;
 
   const unsigned short NOTIFICATION_REMOTE_HELD = 0;
   const unsigned short NOTIFICATION_REMOTE_RESUMED = 1;
 
+  readonly attribute unsigned long defaultServiceId;
+
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'telephony' permission is allowed to register.
    */
   void registerListener(in nsITelephonyListener listener);
   void unregisterListener(in nsITelephonyListener listener);
 
--- a/dom/telephony/test/marionette/manifest.ini
+++ b/dom/telephony/test/marionette/manifest.ini
@@ -38,8 +38,9 @@ disabled = Bug 820802
 disabled = Bug 821966
 [test_redundant_operations.js]
 disabled = Bug 821927
 [test_multiple_hold.js]
 disabled = Bug 821958
 [test_outgoing_emergency_in_airplane_mode.js]
 [test_emergency_label.js]
 [test_conference.js]
+[test_dsds_default_service_id.js]
new file mode 100644
--- /dev/null
+++ b/dom/telephony/test/marionette/test_dsds_default_service_id.js
@@ -0,0 +1,127 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+
+const TELEPHONY_PROVIDER_CONTRACTID =
+  "@mozilla.org/telephony/telephonyprovider;1";
+
+const PREF_RIL_NUM_RADIO_INTERFACES = "ril.numRadioInterfaces";
+const PREF_DEFAULT_SERVICE_ID = "dom.telephony.defaultServiceId";
+
+function setPrefAndVerify(prefKey, setVal, service, attrName, expectedVal, deferred) {
+  log("  Set '" + prefKey + "' to " + setVal);
+  Services.prefs.setIntPref(prefKey, setVal);
+  let prefVal = Services.prefs.getIntPref(prefKey);
+  is(prefVal, setVal, "'" + prefKey + "' set to " + setVal);
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, expectedVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+}
+
+function getNumRadioInterfaces() {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    let numRil = Services.prefs.getIntPref(PREF_RIL_NUM_RADIO_INTERFACES);
+    log("numRil = " + numRil);
+
+    deferred.resolve(numRil);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function getService(contractId, ifaceName) {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    log("Getting service for " + ifaceName);
+    let service = Cc[contractId].getService(Ci[ifaceName]);
+    ok(service, "service.constructor is " + service.constructor);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkInitialEquality(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  log("  Checking initial value for '" + prefKey + "'");
+  let origPrefVal = Services.prefs.getIntPref(prefKey);
+  ok(isFinite(origPrefVal), "default '" + prefKey + "' value");
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, origPrefVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkSetToNegtiveValue(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  // Set to -1 and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, -1, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkSetToOverflowedValue(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  // Set to larger-equal than numRil and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, numRil, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkValueChange(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  if (numRil > 1) {
+    // Set to (numRil - 1) and verify defaultVal equals.
+    setPrefAndVerify(prefKey, numRil - 1, service, attrName, numRil - 1, deferred);
+  } else {
+    window.setTimeout(function () {
+      deferred.resolve(service);
+    }, 0);
+  }
+
+  return deferred.promise;
+}
+
+function verify(contractId, ifaceName, attrName, prefKey, numRil) {
+  let deferred = Promise.defer();
+
+  getService(contractId, ifaceName)
+    .then(checkInitialEquality.bind(null, attrName, prefKey))
+    .then(checkSetToNegtiveValue.bind(null, attrName, prefKey))
+    .then(checkSetToOverflowedValue.bind(null, attrName, prefKey, numRil))
+    .then(checkValueChange.bind(null, attrName, prefKey, numRil))
+    .then(function () {
+      // Reset.
+      Services.prefs.clearUserPref(prefKey);
+
+      deferred.resolve(numRil);
+    });
+
+  return deferred.promise;
+}
+
+getNumRadioInterfaces()
+  .then(verify.bind(null, TELEPHONY_PROVIDER_CONTRACTID, "nsITelephonyProvider",
+                    "defaultServiceId", PREF_DEFAULT_SERVICE_ID))
+  .then(finish);
--- a/dom/voicemail/nsIVoicemailProvider.idl
+++ b/dom/voicemail/nsIVoicemailProvider.idl
@@ -17,19 +17,21 @@ interface nsIVoicemailListener : nsISupp
    */
   void notifyStatusChanged(in nsIDOMMozVoicemailStatus status);
 };
 
 /**
  * XPCOM component (in the content process) that provides the voicemail
  * information.
  */
-[scriptable, uuid(37bc0991-21a3-4de9-b888-d667fea6c05d)]
+[scriptable, uuid(38746f3c-f4e3-4804-b900-ba2463b923c8)]
 interface nsIVoicemailProvider : nsISupports
 {
+  readonly attribute unsigned long voicemailDefaultServiceId;
+
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'voicemail' permission is allowed to register.
    */
   void registerVoicemailMsg(in nsIVoicemailListener listener);
   void unregisterVoicemailMsg(in nsIVoicemailListener listener);
 
--- a/dom/voicemail/test/marionette/manifest.ini
+++ b/dom/voicemail/test/marionette/manifest.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = true
 
 [test_voicemail_statuschanged.py]
 [test_voicemail_number.js]
+[test_dsds_default_service_id.js]
new file mode 100644
--- /dev/null
+++ b/dom/voicemail/test/marionette/test_dsds_default_service_id.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+
+const VOICEMAIL_PROVIDER_CONTRACTID = "@mozilla.org/ril/content-helper;1";
+
+const PREF_RIL_NUM_RADIO_INTERFACES = "ril.numRadioInterfaces";
+const PREF_DEFAULT_SERVICE_ID = "dom.voicemail.defaultServiceId";
+
+function setPrefAndVerify(prefKey, setVal, service, attrName, expectedVal, deferred) {
+  log("  Set '" + prefKey + "' to " + setVal);
+  Services.prefs.setIntPref(prefKey, setVal);
+  let prefVal = Services.prefs.getIntPref(prefKey);
+  is(prefVal, setVal, "'" + prefKey + "' set to " + setVal);
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, expectedVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+}
+
+function getNumRadioInterfaces() {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    let numRil = Services.prefs.getIntPref(PREF_RIL_NUM_RADIO_INTERFACES);
+    log("numRil = " + numRil);
+
+    deferred.resolve(numRil);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function getService(contractId, ifaceName) {
+  let deferred = Promise.defer();
+
+  window.setTimeout(function () {
+    log("Getting service for " + ifaceName);
+    let service = Cc[contractId].getService(Ci[ifaceName]);
+    ok(service, "service.constructor is " + service.constructor);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkInitialEquality(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  log("  Checking initial value for '" + prefKey + "'");
+  let origPrefVal = Services.prefs.getIntPref(prefKey);
+  ok(isFinite(origPrefVal), "default '" + prefKey + "' value");
+
+  window.setTimeout(function () {
+    let defaultVal = service[attrName];
+    is(defaultVal, origPrefVal, attrName);
+
+    deferred.resolve(service);
+  }, 0);
+
+  return deferred.promise;
+}
+
+function checkSetToNegtiveValue(attrName, prefKey, service) {
+  let deferred = Promise.defer();
+
+  // Set to -1 and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, -1, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkSetToOverflowedValue(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  // Set to larger-equal than numRil and verify defaultVal == 0.
+  setPrefAndVerify(prefKey, numRil, service, attrName, 0, deferred);
+
+  return deferred.promise;
+}
+
+function checkValueChange(attrName, prefKey, numRil, service) {
+  let deferred = Promise.defer();
+
+  if (numRil > 1) {
+    // Set to (numRil - 1) and verify defaultVal equals.
+    setPrefAndVerify(prefKey, numRil - 1, service, attrName, numRil - 1, deferred);
+  } else {
+    window.setTimeout(function () {
+      deferred.resolve(service);
+    }, 0);
+  }
+
+  return deferred.promise;
+}
+
+function verify(contractId, ifaceName, attrName, prefKey, numRil) {
+  let deferred = Promise.defer();
+
+  getService(contractId, ifaceName)
+    .then(checkInitialEquality.bind(null, attrName, prefKey))
+    .then(checkSetToNegtiveValue.bind(null, attrName, prefKey))
+    .then(checkSetToOverflowedValue.bind(null, attrName, prefKey, numRil))
+    .then(checkValueChange.bind(null, attrName, prefKey, numRil))
+    .then(function () {
+      // Reset.
+      Services.prefs.clearUserPref(prefKey);
+
+      deferred.resolve(numRil);
+    });
+
+  return deferred.promise;
+}
+
+getNumRadioInterfaces()
+  .then(verify.bind(null, VOICEMAIL_PROVIDER_CONTRACTID, "nsIVoicemailProvider",
+                    "voicemailDefaultServiceId", PREF_DEFAULT_SERVICE_ID))
+  .then(finish);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -4295,17 +4295,19 @@ pref("dom.vibrator.max_vibrate_list_len"
 pref("dom.battery.enabled", true);
 
 // WebSMS
 pref("dom.sms.enabled", false);
 // Enable Latin characters replacement with corresponding ones in GSM SMS
 // 7-bit default alphabet.
 pref("dom.sms.strict7BitEncoding", false);
 pref("dom.sms.requestStatusReport", true);
-pref("dom.mms.requestStatusReport", true);
+// Numeric default service id for SMS API calls with |serviceId| parameter
+// omitted.
+pref("dom.sms.defaultServiceId", 0);
 
 // WebContacts
 pref("dom.mozContacts.enabled", false);
 pref("dom.navigator-property.disable.mozContacts", true);
 pref("dom.global-constructor.disable.mozContact", true);
 
 // WebAlarms
 pref("dom.mozAlarms.enabled", false);
@@ -4421,32 +4423,39 @@ pref("dom.placeholder.show_on_focus", tr
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
 
 // MMS version 1.1 = 0x11 (or decimal 17)
 // MMS version 1.3 = 0x13 (or decimal 19)
 // @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.34
 pref("dom.mms.version", 19);
 
+pref("dom.mms.requestStatusReport", true);
+
 // Retrieval mode for MMS
 // manual: Manual retrieval mode.
 // automatic: Automatic retrieval mode even in roaming.
 // automatic-home: Automatic retrieval mode in home network.
 // never: Never retrieval mode.
 pref("dom.mms.retrieval_mode", "manual");
 
 pref("dom.mms.sendRetryCount", 3);
 pref("dom.mms.sendRetryInterval", 300000);
 
 pref("dom.mms.retrievalRetryCount", 4);
 pref("dom.mms.retrievalRetryIntervals", "60000,300000,600000,1800000");
-
+// Numeric default service id for MMS API calls with |serviceId| parameter
+// omitted.
+pref("dom.mms.defaultServiceId", 0);
 // Debug enabler for MMS.
 pref("mms.debugging.enabled", false);
 
+// Number of RadioInterface instances to create.
+pref("ril.numRadioInterfaces", 0);
+
 // If the user puts a finger down on an element and we think the user
 // might be executing a pan gesture, how long do we wait before
 // tentatively deciding the gesture is actually a tap and activating
 // the target element?
 pref("ui.touch_activation.delay_ms", 100);
 
 // If the user has clicked an element, how long do we keep the
 // :active state before it is cleared by the mouse sequences
@@ -4472,28 +4481,34 @@ pref("dom.forms.inputmode", true);
 // InputMethods for soft keyboards in B2G
 pref("dom.mozInputMethod.enabled", false);
 
 // DataStore is disabled by default
 pref("dom.datastore.enabled", false);
 
 // Telephony API
 pref("dom.telephony.enabled", false);
+// Numeric default service id for WebTelephony API calls with |serviceId|
+// parameter omitted.
+pref("dom.telephony.defaultServiceId", 0);
 
 // Cell Broadcast API
 pref("dom.cellbroadcast.enabled", false);
 
 // ICC API
 pref("dom.icc.enabled", false);
 
 // Mobile Connection API
 pref("dom.mobileconnection.enabled", false);
 
 // Voice Mail API
 pref("dom.voicemail.enabled", false);
+// Numeric default service id for Voice Mail API calls with |serviceId|
+// parameter omitted.
+pref("dom.voicemail.defaultServiceId", 0);
 
 // DOM Inter-App Communication API.
 pref("dom.inter-app-communication-api.enabled", false);
 
 // The tables used for Safebrowsing phishing and malware checks.
 pref("urlclassifier.malware_table", "goog-malware-shavar");
 pref("urlclassifier.phish_table", "goog-phish-shavar");
 pref("urlclassifier.download_block_table", "goog-badbinurl-shavar");