Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 17 Sep 2014 14:43:42 -0400
changeset 205897 0c796a435ec069935cfb4ab980f4e83c7cbd264e
parent 205896 3774cabb49f23b15d4be7f5649b4f6fb59955385 (current diff)
parent 205836 d2c01d77b9d0bf344d3a570dd224c4c543f47bba (diff)
child 205898 57b5abc0d381d714a71597959df038ba3aee78cc
push id27507
push userryanvm@gmail.com
push dateThu, 18 Sep 2014 02:16:54 +0000
treeherdermozilla-central@488d490da742 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. a=merge
browser/base/content/test/general/browser_bug1047603.js
dom/mobileconnection/gonk/MobileConnectionService.js
dom/mobileconnection/gonk/MobileConnectionService.manifest
dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl
--- a/b2g/components/AlertsHelper.jsm
+++ b/b2g/components/AlertsHelper.jsm
@@ -220,31 +220,33 @@ let AlertsHelper = {
     try {
       let data = Cu.cloneInto(dataObj, {});
     } catch(e) { dataObj = null; }
 
     return dataObj;
   },
 
   showNotification: function(imageURL, title, text, textClickable, cookie,
-                             uid, bidi, lang, dataObj, manifestURL, timestamp) {
+                             uid, bidi, lang, dataObj, manifestURL, timestamp,
+                             behavior) {
     function send(appName, appIcon) {
       SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
         type: kDesktopNotification,
         id: uid,
         icon: imageURL,
         title: title,
         text: text,
         bidi: bidi,
         lang: lang,
         appName: appName,
         appIcon: appIcon,
         manifestURL: manifestURL,
         timestamp: timestamp,
-        data: dataObj
+        data: dataObj,
+        mozbehavior: behavior
       });
     }
 
     if (!manifestURL || !manifestURL.length) {
       send(null, null);
       return;
     }
 
@@ -287,17 +289,17 @@ let AlertsHelper = {
       tag: details.tag || undefined,
       timestamp: details.timestamp || undefined,
       dataObj: dataObject || undefined
     };
     this.registerAppListener(data.uid, listener);
     this.showNotification(data.imageURL, data.title, data.text,
                           details.textClickable, null, data.uid, details.dir,
                           details.lang, dataObject, details.manifestURL,
-                          details.timestamp);
+                          details.timestamp, details.mozbehavior);
   },
 
   closeAlert: function(name) {
     SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
       type: kDesktopNotificationClose,
       id: name
     });
   },
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="4d1e85908d792d9468c4da7040acd191fbb51b40">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="4d1e85908d792d9468c4da7040acd191fbb51b40">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "9d4cf6d16100e4091fa14530e44ef2cdb5c0f832", 
+    "revision": "9fffcfaa0e101a04a867ee3cbab0f59805e7cb45", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="4d1e85908d792d9468c4da7040acd191fbb51b40">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="4d1e85908d792d9468c4da7040acd191fbb51b40">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="4d1e85908d792d9468c4da7040acd191fbb51b40">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="50666fa8bbbf3d346faff24f92ad8140a44a49d0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="72262d054ffa5d0d2b5a0033f713149281511aea"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -433,25 +433,25 @@
 @BINPATH@/components/ResourceStatsManager.js
 @BINPATH@/components/ResourceStatsManager.manifest
 #endif // MOZ_WIDGET_GONK
 
 ; RIL
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 @BINPATH@/components/MmsService.js
 @BINPATH@/components/MmsService.manifest
-@BINPATH@/components/MobileConnectionService.js
-@BINPATH@/components/MobileConnectionService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/TelephonyService.js
 @BINPATH@/components/TelephonyService.manifest
+@BINPATH@/components/MobileConnectionGonkService.js
+@BINPATH@/components/MobileConnectionGonkService.manifest
 #endif // MOZ_WIDGET_GONK && MOZ_B2G_RIL
 
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amInstallTrigger.js
 @BINPATH@/components/amWebInstallListener.js
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -42,16 +42,20 @@ var tabPreviews = {
       img.src = PageThumbs.getThumbnailURL(uri);
       return img;
     }
 
     return this.capture(aTab, !aTab.hasAttribute("busy"));
   },
 
   capture: function tabPreviews_capture(aTab, aShouldCache) {
+    // Bug 863512 - Make page thumbnails work in electrolysis
+    if (gMultiProcessBrowser)
+      return new Image();
+
     let browser = aTab.linkedBrowser;
     let uri = browser.currentURI.spec;
 
     // FIXME: The gBrowserThumbnails._shouldCapture determines whether
     //        thumbnails should be written to disk. This should somehow be part
     //        of the PageThumbs API. (bug 1062414)
     if (aShouldCache &&
         gBrowserThumbnails._shouldCapture(browser)) {
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1210,8 +1210,18 @@ toolbarpaletteitem[place="palette"][hidd
   animation-duration: 2s;
 }
 
 /* Combined context-menu items */
 #context-navigation > .menuitem-iconic > .menu-iconic-text,
 #context-navigation > .menuitem-iconic > .menu-accel-container {
   display: none;
 }
+
+/* Tracking protection doorhanger */
+.popup-notification-footer[popupid="bad-content"] {
+  display: none;
+}
+
+.popup-notification-footer[popupid="bad-content"][mixedblockdisabled],
+.popup-notification-footer[popupid="bad-content"][trackingblockdisabled] {
+  display: block;
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4129,17 +4129,17 @@ var TabsProgressListener = {
   }
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
-  _openURIInNewTab: function(aURI, aOpener, aIsExternal, aEnsureNonRemote=false) {
+  _openURIInNewTab: function(aURI, aOpener, aIsExternal) {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
       let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window);
       win = RecentWindow.getMostRecentBrowserWindow({private: isPrivate});
@@ -4161,40 +4161,23 @@ nsBrowserAccess.prototype = {
     let referrer = aOpener ? makeURI(aOpener.location.href) : null;
 
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       referrerURI: referrer,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground});
     let browser = win.gBrowser.getBrowserForTab(tab);
 
-    // It's possible that we've been asked to open a new non-remote
-    // browser in a window that defaults to having remote browsers -
-    // this can happen if we're opening the new tab due to a window.open
-    // or _blank anchor in a non-remote browser. If so, we have to force
-    // the newly opened browser to also not be remote.
-    if (win.gMultiProcessBrowser && aEnsureNonRemote) {
-      win.gBrowser.updateBrowserRemoteness(browser, false);
-    }
-
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
   openURI: function (aURI, aOpener, aWhere, aContext) {
-    // This function should only ever be called if we're opening a URI
-    // from a non-remote browser window (via nsContentTreeOwner).
-    if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
-      Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
-                     "openURI should only ever be called from non-remote browsers.");
-      throw Cr.NS_ERROR_FAILURE;
-    }
-
     var newWindow = null;
     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
       dump("use -chrome command-line option to load external chrome urls\n");
       return null;
     }
 
@@ -4210,17 +4193,17 @@ nsBrowserAccess.prototype = {
         // FIXME: Bug 408379. So how come this doesn't send the
         // referrer like the other loads do?
         var url = aURI ? aURI.spec : "about:blank";
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
-        let browser = this._openURIInNewTab(aURI, aOpener, isExternal, true);
+        let browser = this._openURIInNewTab(aURI, aOpener, isExternal);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = content;
         if (aURI) {
           let referrer = aOpener ? makeURI(aOpener.location.href) : null;
           let loadflags = isExternal ?
--- a/browser/base/content/searchSuggestionUI.css
+++ b/browser/base/content/searchSuggestionUI.css
@@ -20,29 +20,25 @@
 
 .searchSuggestionRow {
   cursor: default;
   margin: 0;
   max-width: inherit;
   padding: 0;
 }
 
-.searchSuggestionRow.formHistory {
-  color: hsl(210,100%,40%);
+.searchSuggestionRow.formHistory + .searchSuggestionRow.remote > td {
+  border-top: 1px solid GrayText;
 }
 
 .searchSuggestionRow.selected {
   background-color: hsl(210,100%,40%);
   color: hsl(0,0%,100%);
 }
 
 .searchSuggestionEntry {
   margin: 0;
   max-width: inherit;
   overflow: hidden;
   padding: 6px 8px;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
-
-.searchSuggestionEntry > span.typed {
-  font-weight: bold;
-}
--- a/browser/base/content/searchSuggestionUI.js
+++ b/browser/base/content/searchSuggestionUI.js
@@ -218,23 +218,16 @@ SearchSuggestionUIController.prototype =
   _onFocus: function () {
     this._speculativeConnect();
   },
 
   _onBlur: function () {
     this._hideSuggestions();
   },
 
-  _onMousemove: function (event) {
-    // It's important to listen for mousemove, not mouseover or mouseenter.  The
-    // latter two are triggered when the user is typing and the mouse happens to
-    // be over the suggestions popup.
-    this.selectedIndex = this._indexOfTableRowOrDescendent(event.target);
-  },
-
   _onMousedown: function (event) {
     let idx = this._indexOfTableRowOrDescendent(event.target);
     let suggestion = this.suggestionAtIndex(idx);
     this._stickyInputValue = suggestion;
     this.input.value = suggestion;
     this.input.setAttribute("selection-index", idx);
     this.input.setAttribute("selection-kind", "mouse");
     this._hideSuggestions();
@@ -302,17 +295,16 @@ SearchSuggestionUIController.prototype =
     }
   },
 
   _makeTableRow: function (type, suggestionStr, currentRow, searchWords) {
     let row = document.createElementNS(HTML_NS, "tr");
     row.classList.add("searchSuggestionRow");
     row.classList.add(type);
     row.setAttribute("role", "presentation");
-    row.addEventListener("mousemove", this);
     row.addEventListener("mousedown", this);
 
     let entry = document.createElementNS(HTML_NS, "td");
     entry.classList.add("searchSuggestionEntry");
     entry.setAttribute("role", "option");
     entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
     entry.setAttribute("aria-selected", "false");
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -478,10 +478,8 @@ skip-if = e10s # Bug 516755 - SessionSto
 skip-if = e10s
 [browser_bug1024133-switchtab-override-keynav.js]
 skip-if = e10s
 [browser_bug1025195_switchToTabHavingURI_ignoreFragment.js]
 [browser_addCertException.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (content.document.getElementById)
 [browser_bug1045809.js]
 skip-if = e10s
-[browser_bug1047603.js]
-skip-if = os == "linux" # Bug 1066856 - waiting for OMTC to be enabled by default on Linux.
deleted file mode 100644
--- a/browser/base/content/test/general/browser_bug1047603.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const OPEN_LOCATION_PREF = "browser.link.open_newwindow";
-const NON_REMOTE_PAGE = "about:crashes";
-
-const SIMPLE_PAGE_HTML = `
-<a href="about:home" target="_blank" id="testAnchor">Open a window</a>
-`;
-
-function frame_script() {
-  addMessageListener("test:click", (message) => {
-    let element = content.document.getElementById("testAnchor");
-    element.click();
-  });
-  sendAsyncMessage("test:ready");
-}
-
-/**
- * Returns a Promise that resolves once the frame_script is loaded
- * in the browser, and has seen the DOMContentLoaded event.
- */
-function waitForFrameScriptReady(mm) {
-  return new Promise((resolve, reject) => {
-    mm.addMessageListener("test:ready", function onTestReady() {
-      mm.removeMessageListener("test:ready", onTestReady);
-      resolve();
-    });
-  });
-}
-
-/**
- * Takes some browser in some window, and forces that browser
- * to become non-remote, and then navigates it to a page that
- * we're not supposed to be displaying remotely. Returns a
- * Promise that resolves when the browser is no longer remote.
- */
-function prepareNonRemoteBrowser(aWindow, browser) {
-  aWindow.gBrowser.updateBrowserRemoteness(browser, false);
-  browser.loadURI(NON_REMOTE_PAGE);
-  return new Promise((resolve, reject) => {
-    waitForCondition(() => !browser.isRemoteBrowser, () => {
-      resolve();
-    }, "Waiting for browser to become non-remote");
-  })
-}
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref(OPEN_LOCATION_PREF);
-});
-
-/**
- * Test that if we open a new tab from a link in a non-remote
- * browser in an e10s window, that the new tab's browser is also
- * not remote. Also tests with a private browsing window.
- */
-add_task(function* test_new_tab() {
-  let normalWindow = yield promiseOpenAndLoadWindow({
-    remote: true
-  }, true);
-  let privateWindow = yield promiseOpenAndLoadWindow({
-    remote: true,
-    private: true,
-  }, true);
-
-  for (let testWindow of [normalWindow, privateWindow]) {
-    let testBrowser = testWindow.gBrowser.selectedBrowser;
-    yield prepareNonRemoteBrowser(testWindow, testBrowser);
-
-    // Get our framescript ready
-    let mm = testBrowser.messageManager;
-    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
-    let readyPromise = waitForFrameScriptReady(mm);
-    yield readyPromise;
-
-    // Inject our test HTML into our non-remote tab.
-    testBrowser.contentDocument.body.innerHTML = SIMPLE_PAGE_HTML;
-
-    // Click on the link in the browser, and wait for the new tab.
-    mm.sendAsyncMessage("test:click");
-    let tabOpenEvent = yield waitForNewTab(testWindow.gBrowser);
-    let newTab = tabOpenEvent.target;
-    ok(!newTab.linkedBrowser.isRemoteBrowser,
-       "The opened browser should not be remote.");
-
-    testWindow.gBrowser.removeTab(newTab);
-  }
-
-  normalWindow.close();
-  privateWindow.close();
-});
-
-/**
- * Test that if we open a new window from a link in a non-remote
- * browser in an e10s window, that the new window is not an e10s
- * window. Also tests with a private browsing window.
- */
-add_task(function* test_new_window() {
-  let normalWindow = yield promiseOpenAndLoadWindow({
-    remote: true
-  }, true);
-  let privateWindow = yield promiseOpenAndLoadWindow({
-    remote: true,
-    private: true,
-  }, true);
-
-  // Fiddle with the prefs so that we open target="_blank" links
-  // in new windows instead of new tabs.
-  Services.prefs.setIntPref(OPEN_LOCATION_PREF,
-                            Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW);
-
-  for (let testWindow of [normalWindow, privateWindow]) {
-    let testBrowser = testWindow.gBrowser.selectedBrowser;
-    yield prepareNonRemoteBrowser(testWindow, testBrowser);
-
-    // Get our framescript ready
-    let mm = testBrowser.messageManager;
-    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
-    let readyPromise = waitForFrameScriptReady(mm);
-    yield readyPromise;
-
-    // Inject our test HTML into our non-remote window.
-    testBrowser.contentDocument.body.innerHTML = SIMPLE_PAGE_HTML;
-
-    // Click on the link in the browser, and wait for the new window.
-    let windowOpenPromise = promiseTopicObserved("browser-delayed-startup-finished");
-    mm.sendAsyncMessage("test:click");
-    let [newWindow] = yield windowOpenPromise;
-    ok(!newWindow.gMultiProcessBrowser,
-       "The opened window should not be an e10s window.");
-    newWindow.close();
-  }
-
-  normalWindow.close();
-  privateWindow.close();
-
-  Services.prefs.clearUserPref(OPEN_LOCATION_PREF);
-});
--- a/browser/base/content/test/general/browser_searchSuggestionUI.js
+++ b/browser/base/content/test/general/browser_searchSuggestionUI.js
@@ -97,24 +97,16 @@ add_task(rightArrowOrReturn("VK_RIGHT"))
 add_task(rightArrowOrReturn("VK_RETURN"));
 
 add_task(function* mouse() {
   yield setUp();
 
   let state = yield msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
-  // Mouse over the first suggestion.
-  state = yield msg("mousemove", 0);
-  checkState(state, "xfoo", ["xfoo", "xbar"], 0);
-
-  // Mouse over the second suggestion.
-  state = yield msg("mousemove", 1);
-  checkState(state, "xbar", ["xfoo", "xbar"], 1);
-
   // Click the second suggestion.  This should make it sticky.  To make sure it
   // sticks, trigger suggestions again and cycle through them by pressing Down
   // until nothing is selected again.
   state = yield msg("mousedown", 1);
   checkState(state, "xbar", [], -1);
 
   state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
   checkState(state, "xbar", ["xbarfoo", "xbarbar"], -1);
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -665,12 +665,8 @@ function assertWebRTCIndicatorStatus(exp
     }
   }
 }
 
 function makeActionURI(action, params) {
   let url = "moz-action:" + action + "," + JSON.stringify(params);
   return NetUtil.newURI(url);
 }
-
-function waitForNewTab(aTabBrowser) {
-  return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
-}
--- a/browser/base/content/test/general/searchSuggestionUI.js
+++ b/browser/base/content/test/general/searchSuggestionUI.js
@@ -32,43 +32,16 @@ let messageHandlers = {
     ack();
   },
 
   blur: function () {
     gController.input.blur();
     ack();
   },
 
-  mousemove: function (suggestionIdx) {
-    // Copied from widget/tests/test_panel_mouse_coords.xul and
-    // browser/base/content/test/newtab/head.js
-    let row = gController._table.children[suggestionIdx];
-    let rect = row.getBoundingClientRect();
-    let left = content.mozInnerScreenX + rect.left;
-    let x = left + rect.width / 2;
-    let y = content.mozInnerScreenY + rect.top + rect.height / 2;
-
-    let utils = content.SpecialPowers.getDOMWindowUtils(content);
-    let scale = utils.screenPixelsPerCSSPixel;
-
-    let widgetToolkit = content.SpecialPowers.
-                        Cc["@mozilla.org/xre/app-info;1"].
-                        getService(content.SpecialPowers.Ci.nsIXULRuntime).
-                        widgetToolkit;
-    let nativeMsg = widgetToolkit == "cocoa" ? 5 : // NSMouseMoved
-                    widgetToolkit == "windows" ? 1 : // MOUSEEVENTF_MOVE
-                    3; // GDK_MOTION_NOTIFY
-
-    row.addEventListener("mousemove", function onMove() {
-      row.removeEventListener("mousemove", onMove);
-      ack();
-    });
-    utils.sendNativeMouseEvent(x * scale, y * scale, nativeMsg, 0, null);
-  },
-
   mousedown: function (suggestionIdx) {
     gController.onClick = () => {
       gController.onClick = null;
       ack();
     };
     let row = gController._table.children[suggestionIdx];
     content.sendMouseEvent({ type: "mousedown" }, row);
   },
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == "mulet" || e10s # Bug 915547 (social providers don't install)
+skip-if = buildapp == "mulet"
 support-files =
   blocklist.xml
   checked.jpg
   head.js
   opengraph/og_invalid_url.html
   opengraph/opengraph.html
   opengraph/shortlink_linkrel.html
   opengraph/shorturl_link.html
@@ -21,26 +21,36 @@ support-files =
   social_postActivation.html
   social_sidebar.html
   social_sidebar_empty.html
   social_window.html
   social_worker.js
   unchecked.jpg
 
 [browser_aboutHome_activation.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_addons.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_blocklist.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_share.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_activation.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_chatwindow.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_chatwindow_resize.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_chatwindowfocus.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_errorPage.js]
 [browser_social_flyout.js]
 [browser_social_isVisible.js]
 [browser_social_marks.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_multiprovider.js]
 [browser_social_multiworker.js]
 [browser_social_perwindowPB.js]
 [browser_social_sidebar.js]
 [browser_social_status.js]
+skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_window.js]
 [browser_social_workercrash.js]
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -31,17 +31,20 @@ function goOnline(callback) {
     BrowserOffline.toggleOfflineStatus();
   if (callback)
     callback();
 }
 
 function openPanel(url, panelCallback, loadCallback) {
   // open a flyout
   SocialFlyout.open(url, 0, panelCallback);
-  SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad() {
+  SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad(evt) {
+    if (evt.target != SocialFlyout.panel.firstChild.contentDocument) {
+      return;
+    }
     SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
     loadCallback();
   }, true);
 }
 
 function openChat(url, panelCallback, loadCallback) {
   // open a chat window
   let chatbar = getChatBar();
@@ -140,18 +143,20 @@ var tests = {
             function() { // the panel api callback
               panelCallbackCount++;
             },
             function() { // the "load" callback.
               executeSoon(function() {
                 todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
                 ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
                 gc();
-                SocialFlyout.unload();
-                next();
+                executeSoon(function() {
+                  SocialFlyout.unload();
+                  next();
+                });
               });
             }
           );
         });
       }
     );
   },
 
--- a/browser/base/content/test/social/browser_social_marks.js
+++ b/browser/base/content/test/social/browser_social_marks.js
@@ -68,17 +68,17 @@ function test() {
     });
   });
 }
 
 var tests = {
   testButtonDisabledOnActivate: function(next) {
     // starting on about:blank page, share should be visible but disabled when
     // adding provider
-    is(gBrowser.contentDocument.location.href, "about:blank");
+    is(gBrowser.selectedTab.linkedBrowser.currentURI.spec, "about:blank");
     SocialService.addProvider(manifest2, function(provider) {
       is(provider.origin, manifest2.origin, "provider is installed");
       let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
       let widget = CustomizableUI.getWidget(id).forWindow(window)
       ok(widget.node, "button added to widget set");
 
       // bypass widget go directly to dom, check attribute states
       let button = document.getElementById(id);
--- a/browser/base/content/test/social/browser_social_multiprovider.js
+++ b/browser/base/content/test/social/browser_social_multiprovider.js
@@ -79,15 +79,18 @@ var tests = {
 
 function checkUIStateMatchesProvider(provider) {
   // Sidebar
   is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
-  sbrowser.addEventListener("load", function load() {
+  sbrowser.addEventListener("load", function load(evt) {
+    if (evt.target != sbrowser.contentDocument) {
+      return;
+    }
     sbrowser.removeEventListener("load", load, true);
     // give the load a chance to finish before pulling the rug (ie. calling
     // next)
     executeSoon(callback);
   }, true);
 }
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1645,17 +1645,17 @@
             <xul:separator class="groove"/>
             <xul:hbox align="start">
               <xul:vbox>
                 <xul:description class="popup-notification-item-title"
                   xbl:inherits="popupid">
                   &mixedContentBlocked2.message;
                 </xul:description>
                 <xul:description class="popup-notification-item-message"
-                  xbl:inherits="popupid">
+                  xbl:inherits="popupid,mixedblockdisabled">
                   &mixedContentBlocked2.moreinfo;
                 </xul:description>
                 <xul:label anonid="mixedContent.helplink"
                   class="text-link plain" href=""
                   value="&mixedContentBlocked2.learnMore;"/>
               </xul:vbox>
               <xul:button
                 type="menu" label="&mixedContentBlocked2.options;"
@@ -1667,34 +1667,33 @@
                     oncommand="document.getBindingParent(this).disableMixedContentProtection();"/>
                   <xul:menuitem anonid="mixedContentAction.block"
                     hidden="true" label="&mixedContentBlocked2.block.label;"
                     accesskey="&mixedContentBlocked2.block.accesskey;"
                     oncommand="document.getBindingParent(this).enableMixedContentProtection();"/>
                 </xul:menupopup>
               </xul:button>
             </xul:hbox>
-            <xul:hbox anonid="mixedContentProtectionDisabled" hidden="true"
-                      class="popup-notification-footer" xbl:inherits="popupid">
+            <xul:hbox class="popup-notification-footer" xbl:inherits="popupid,mixedblockdisabled">
               <xul:description class="popup-notification-item-message popup-notification-item-message-critical" xbl:inherits="popupid">
                   &mixedContentBlocked2.disabled.message;
               </xul:description>
             </xul:hbox>
           </xul:vbox>
           <!-- tracking content -->
           <xul:vbox anonid="trackingContent" hidden="true">
             <xul:separator class="groove"/>
             <xul:hbox align="start">
               <xul:vbox>
                 <xul:description class="popup-notification-item-title"
                   xbl:inherits="popupid">
                   &trackingContentBlocked.message;
                 </xul:description>
                 <xul:description class="popup-notification-item-message"
-                  xbl:inherits="popupid">
+                  xbl:inherits="popupid,trackingblockdisabled">
                   &trackingContentBlocked.moreinfo;
                 </xul:description>
                 <xul:label anonid="trackingContent.helplink"
                   class="text-link plain" href=""
                   value="&trackingContentBlocked.learnMore;"/>
               </xul:vbox>
               <xul:button
                 type="menu" label="&trackingContentBlocked.options;"
@@ -1706,18 +1705,17 @@
                     oncommand="document.getBindingParent(this).disableTrackingContentProtection();"/>
                   <xul:menuitem anonid="trackingContentAction.block"
                     hidden="true" label="&trackingContentBlocked.block.label;"
                     accesskey="&trackingContentBlocked.block.accesskey;"
                     oncommand="document.getBindingParent(this).enableTrackingContentProtection();"/>
                 </xul:menupopup>
               </xul:button>
             </xul:hbox>
-            <xul:hbox anonid="trackingContentProtectionDisabled" hidden="true"
-                      class="popup-notification-footer" xbl:inherits="popupid">
+            <xul:hbox class="popup-notification-footer" xbl:inherits="popupid,trackingblockdisabled">
               <xul:description class="popup-notification-item-message popup-notification-item-message-critical" xbl:inherits="popupid">
                 &trackingContentBlocked.disabled.message;
                 </xul:description>
             </xul:hbox>
           </xul:vbox>
         </xul:vbox>
         <xul:vbox pack="start">
           <xul:toolbarbutton anonid="closebutton"
@@ -1745,20 +1743,16 @@
       <field name="_mixedContentUnblock">
         document.getAnonymousElementByAttribute(this, "anonid",
           "mixedContentAction.unblock")
       </field>
       <field name="_mixedContentBlock">
         document.getAnonymousElementByAttribute(this, "anonid",
           "mixedContentAction.block");
       </field>
-      <field name="_mixedContentProtectionDisabledWarning">
-        document.getAnonymousElementByAttribute(this, "anonid",
-          "mixedContentProtectionDisabled")
-      </field>
       <field name="_mixedContentHelpLink">
         document.getAnonymousElementByAttribute(this, "anonid",
           "mixedContent.helplink")
       </field>
       <property name="isMixedContentBlocked" readonly="true">
         <getter><![CDATA[
           return this.notification.options.state &
             Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
@@ -1771,20 +1765,16 @@
       <field name="_trackingContentUnblock">
         document.getAnonymousElementByAttribute(this, "anonid",
           "trackingContentAction.unblock")
       </field>
       <field name="_trackingContentBlock">
         document.getAnonymousElementByAttribute(this, "anonid",
           "trackingContentAction.block");
       </field>
-      <field name="_trackingContentProtectionDisabledWarning">
-        document.getAnonymousElementByAttribute(this, "anonid",
-          "trackingContentProtectionDisabled")
-      </field>
       <field name="_trackingContentHelpLink">
         document.getAnonymousElementByAttribute(this, "anonid",
           "trackingContent.helplink")
       </field>
       <property name="isTrackingContentBlocked" readonly="true">
         <getter><![CDATA[
           return this.notification.options.state &
             Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
@@ -1804,39 +1794,39 @@
           _mixedContent.hidden = false;
           _mixedContentUnblock.hidden = false;
           _mixedContentHelpLink.href =
             Services.urlFormatter.formatURLPref("app.support.baseURL")
               + "mixed-content";
         }
         if (this.notification.options.state &
             Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
+          this.setAttribute("mixedblockdisabled", true);
           _mixedContent.hidden = false;
           _mixedContentBlock.hidden = false;
-          _mixedContentProtectionDisabledWarning.hidden = false;
           _mixedContentHelpLink.href =
             Services.urlFormatter.formatURLPref("app.support.baseURL")
               + "mixed-content";
         }
         if (this.notification.options.state &
             Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) {
           _doorhangerTitle.value =
             gNavigatorBundle.getFormattedString(
               "badContentBlocked.blocked.message", [this._brandShortName]);
-          _trackingContent.hidden = false
+          _trackingContent.hidden = false;
           _trackingContentUnblock.hidden = false;
           _trackingContentHelpLink.href =
             Services.urlFormatter.formatURLPref("app.support.baseURL")
               + "tracking-protection";
         }
         if (this.notification.options.state &
             Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) {
-          _trackingContent.hidden = false
+          this.setAttribute("trackingblockdisabled", true);
+          _trackingContent.hidden = false;
           _trackingContentBlock.hidden = false;
-          _trackingContentProtectionDisabledWarning.hidden = false;
           _trackingContentHelpLink.href =
             Services.urlFormatter.formatURLPref("app.support.baseURL")
               + "tracking-protection";
         }
       ]]></constructor>
       <method name="disableMixedContentProtection">
         <body><![CDATA[
           // Use telemetry to measure how often unblocking happens
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -104,16 +104,33 @@ const injectObjectAPI = function(api, ta
 function injectLoopAPI(targetWindow) {
   let ringer;
   let ringerStopper;
   let appVersionInfo;
   let contactsAPI;
 
   let api = {
     /**
+     * Gets an object with data that represents the currently
+     * authenticated user's identity.
+     */
+    userProfile: {
+      enumerable: true,
+      get: function() {
+        if (!MozLoopService.userProfile)
+          return null;
+        let userProfile = Cu.cloneInto({
+          email: MozLoopService.userProfile.email,
+          uid: MozLoopService.userProfile.uid
+        }, targetWindow);
+        return userProfile;
+      }
+    },
+
+    /**
      * Sets and gets the "do not disturb" mode activation flag.
      */
     doNotDisturb: {
       enumerable: true,
       get: function() {
         return MozLoopService.doNotDisturb;
       },
       set: function(aFlag) {
@@ -435,20 +452,34 @@ function injectLoopAPI(targetWindow) {
             OS: appInfo.OS
           }, targetWindow);
         }
         return appVersionInfo;
       }
     },
   };
 
+  function onStatusChanged(aSubject, aTopic, aData) {
+    let event = new targetWindow.CustomEvent("LoopStatusChanged");
+    targetWindow.dispatchEvent(event)
+  };
+
+  function onDOMWindowDestroyed(aSubject, aTopic, aData) {
+    if (targetWindow && aSubject != targetWindow)
+      return;
+    Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed");
+    Services.obs.removeObserver(onStatusChanged, "loop-status-changed");
+  };
+
   let contentObj = Cu.createObjectIn(targetWindow);
   Object.defineProperties(contentObj, api);
   Object.seal(contentObj);
   Cu.makeObjectPropsNormal(contentObj);
+  Services.obs.addObserver(onStatusChanged, "loop-status-changed", false);
+  Services.obs.addObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
 
   targetWindow.navigator.wrappedJSObject.__defineGetter__("mozLoop", function() {
     // We do this in a getter, so that we create these objects
     // only on demand (this is a potential concern, since
     // otherwise we might add one per iframe, and keep them
     // alive for as long as the window is alive).
     delete targetWindow.navigator.wrappedJSObject.mozLoop;
     return targetWindow.navigator.wrappedJSObject.mozLoop = contentObj;
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -41,16 +41,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils",
                                   "resource://services-crypto/utils.js");
 
+XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsProfileClient",
+                                  "resource://gre/modules/FxAccountsProfileClient.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "HawkClient",
                                   "resource://services-common/hawkclient.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
                                   "resource://services-common/hawkrequest.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
@@ -71,16 +74,17 @@ let gRegisteredDeferred = null;
 let gPushHandler = null;
 let gHawkClient = null;
 let gRegisteredLoopServer = false;
 let gLocalizedStrings =  null;
 let gInitializeTimer = null;
 let gFxAOAuthClientPromise = null;
 let gFxAOAuthClient = null;
 let gFxAOAuthTokenData = null;
+let gFxAOAuthProfile = null;
 let gErrors = new Map();
 
 /**
  * Internal helper methods and state
  *
  * The registration is a two-part process. First we need to connect to
  * and register with the push server. Then we need to take the result of that
  * and register with the Loop server.
@@ -879,16 +883,20 @@ this.MozLoopService = {
    * Sets MozLoopService "do not disturb" value.
    *
    * @param {Boolean} aFlag
    */
   set doNotDisturb(aFlag) {
     MozLoopServiceInternal.doNotDisturb = aFlag;
   },
 
+  get userProfile() {
+    return gFxAOAuthProfile;
+  },
+
   get errors() {
     return MozLoopServiceInternal.errors;
   },
 
   /**
    * Returns the current locale
    *
    * @return {String} The code of the current locale.
@@ -999,19 +1007,33 @@ this.MozLoopService = {
       return gRegisteredDeferred.promise.then(Task.async(function*() {
         if (gPushHandler.pushUrl) {
           yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, gPushHandler.pushUrl);
         } else {
           throw new Error("No pushUrl for FxA registration");
         }
         return gFxAOAuthTokenData;
       }));
-    },
-    error => {
+    }).then(tokenData => {
+      let client = new FxAccountsProfileClient({
+        serverURL: gFxAOAuthClient.parameters.profile_uri,
+        token: tokenData.access_token
+      });
+      client.fetchProfile().then(result => {
+        gFxAOAuthProfile = result;
+        MozLoopServiceInternal.notifyStatusChanged();
+      }, error => {
+        console.error("Failed to retrieve profile", error);
+        gFxAOAuthProfile = null;
+        MozLoopServiceInternal.notifyStatusChanged();
+      });
+      return tokenData;
+    }).catch(error => {
       gFxAOAuthTokenData = null;
+      gFxAOAuthProfile = null;
       throw error;
     });
   },
 
   /**
    * Performs a hawk based request to the loop server.
    *
    * @param {LOOP_SESSION_TYPE} sessionType The type of session to use for the request.
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -224,18 +224,17 @@ loop.panel = (function(_, mozL10n) {
         // XXX to be implemented - bug 979845
         navigator.mozLoop.logOutFromFxA();
       } else {
         navigator.mozLoop.logInToFxA();
       }
     },
 
     _isSignedIn: function() {
-      // XXX to be implemented - bug 979845
-      return !!navigator.mozLoop.loggedInToFxA;
+      return !!navigator.mozLoop.userProfile;
     },
 
     render: function() {
       var cx = React.addons.classSet;
       return (
         React.DOM.div({className: "settings-menu dropdown"}, 
           React.DOM.a({className: "btn btn-settings", onClick: this.showDropdownMenu, 
              title: __("settings_menu_button_tooltip")}), 
@@ -444,46 +443,82 @@ loop.panel = (function(_, mozL10n) {
             __("panel_footer_signin_or_signup_link")
           )
         )
       );
     }
   });
 
   /**
+   * FxA user identity (guest/authenticated) component.
+   */
+  var UserIdentity = React.createClass({displayName: 'UserIdentity',
+    render: function() {
+      return (
+        React.DOM.p({className: "user-identity"},
+          this.props.displayName
+        )
+      );
+    }
+  });
+
+  /**
    * Panel view.
    */
   var PanelView = React.createClass({displayName: 'PanelView',
     propTypes: {
       notifications: React.PropTypes.object.isRequired,
       client: React.PropTypes.object.isRequired,
       // Mostly used for UI components showcase and unit tests
-      callUrl: React.PropTypes.string
+      callUrl: React.PropTypes.string,
+      userProfile: React.PropTypes.object,
+    },
+
+    getInitialState: function() {
+      return {
+        userProfile: this.props.userProfile || navigator.mozLoop.userProfile,
+      };
+    },
+
+    _onAuthStatusChange: function() {
+      this.setState({userProfile: navigator.mozLoop.userProfile});
+    },
+
+    componentDidMount: function() {
+      window.addEventListener("LoopStatusChanged", this._onAuthStatusChange);
+    },
+
+    componentWillUnmount: function() {
+      window.removeEventListener("LoopStatusChanged", this._onAuthStatusChange);
     },
 
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
-
+      var displayName = this.state.userProfile && this.state.userProfile.email ||
+                        __("display_name_guest");
       return (
         React.DOM.div(null, 
           NotificationListView({notifications: this.props.notifications, 
                                 clearOnDocumentHidden: true}), 
           TabView({onSelect: this.selectTab}, 
             Tab({name: "call"}, 
               CallUrlResult({client: this.props.client, 
                              notifications: this.props.notifications, 
                              callUrl: this.props.callUrl}), 
               ToSView(null)
             ), 
             Tab({name: "contacts"}, 
               React.DOM.span(null, "contacts")
             )
           ), 
           React.DOM.div({className: "footer"}, 
-            AvailabilityDropdown(null), 
+            React.DOM.div({className: "user-details"},
+              UserIdentity({displayName: displayName}),
+              AvailabilityDropdown(null)
+            ),
             AuthLink(null), 
             SettingsDropdown(null)
           )
         )
       );
     }
   });
 
@@ -538,16 +573,17 @@ loop.panel = (function(_, mozL10n) {
     // Notify the window that we've finished initalization and initial layout
     var evtObject = document.createEvent('Event');
     evtObject.initEvent('loopPanelInitialized', true, false);
     window.dispatchEvent(evtObject);
   }
 
   return {
     init: init,
+    UserIdentity: UserIdentity,
     AvailabilityDropdown: AvailabilityDropdown,
     CallUrlResult: CallUrlResult,
     PanelView: PanelView,
     PanelRouter: PanelRouter,
     SettingsDropdown: SettingsDropdown,
     ToSView: ToSView
   };
 })(_, document.mozL10n);
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -224,18 +224,17 @@ loop.panel = (function(_, mozL10n) {
         // XXX to be implemented - bug 979845
         navigator.mozLoop.logOutFromFxA();
       } else {
         navigator.mozLoop.logInToFxA();
       }
     },
 
     _isSignedIn: function() {
-      // XXX to be implemented - bug 979845
-      return !!navigator.mozLoop.loggedInToFxA;
+      return !!navigator.mozLoop.userProfile;
     },
 
     render: function() {
       var cx = React.addons.classSet;
       return (
         <div className="settings-menu dropdown">
           <a className="btn btn-settings" onClick={this.showDropdownMenu}
              title={__("settings_menu_button_tooltip")} />
@@ -444,46 +443,82 @@ loop.panel = (function(_, mozL10n) {
             {__("panel_footer_signin_or_signup_link")}
           </a>
         </p>
       );
     }
   });
 
   /**
+   * FxA user identity (guest/authenticated) component.
+   */
+  var UserIdentity = React.createClass({
+    render: function() {
+      return (
+        <p className="user-identity">
+          {this.props.displayName}
+        </p>
+      );
+    }
+  });
+
+  /**
    * Panel view.
    */
   var PanelView = React.createClass({
     propTypes: {
       notifications: React.PropTypes.object.isRequired,
       client: React.PropTypes.object.isRequired,
       // Mostly used for UI components showcase and unit tests
-      callUrl: React.PropTypes.string
+      callUrl: React.PropTypes.string,
+      userProfile: React.PropTypes.object,
+    },
+
+    getInitialState: function() {
+      return {
+        userProfile: this.props.userProfile || navigator.mozLoop.userProfile,
+      };
+    },
+
+    _onAuthStatusChange: function() {
+      this.setState({userProfile: navigator.mozLoop.userProfile});
+    },
+
+    componentDidMount: function() {
+      window.addEventListener("LoopStatusChanged", this._onAuthStatusChange);
+    },
+
+    componentWillUnmount: function() {
+      window.removeEventListener("LoopStatusChanged", this._onAuthStatusChange);
     },
 
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
-
+      var displayName = this.state.userProfile && this.state.userProfile.email ||
+                        __("display_name_guest");
       return (
         <div>
           <NotificationListView notifications={this.props.notifications}
                                 clearOnDocumentHidden={true} />
           <TabView onSelect={this.selectTab}>
             <Tab name="call">
               <CallUrlResult client={this.props.client}
                              notifications={this.props.notifications}
                              callUrl={this.props.callUrl} />
               <ToSView />
             </Tab>
             <Tab name="contacts">
               <span>contacts</span>
             </Tab>
           </TabView>
           <div className="footer">
-            <AvailabilityDropdown />
+            <div className="user-details">
+              <UserIdentity displayName={displayName} />
+              <AvailabilityDropdown />
+            </div>
             <AuthLink />
             <SettingsDropdown />
           </div>
         </div>
       );
     }
   });
 
@@ -538,16 +573,17 @@ loop.panel = (function(_, mozL10n) {
     // Notify the window that we've finished initalization and initial layout
     var evtObject = document.createEvent('Event');
     evtObject.initEvent('loopPanelInitialized', true, false);
     window.dispatchEvent(evtObject);
   }
 
   return {
     init: init,
+    UserIdentity: UserIdentity,
     AvailabilityDropdown: AvailabilityDropdown,
     CallUrlResult: CallUrlResult,
     PanelView: PanelView,
     PanelRouter: PanelRouter,
     SettingsDropdown: SettingsDropdown,
     ToSView: ToSView
   };
 })(_, document.mozL10n);
--- a/browser/components/loop/standalone/.gitignore
+++ b/browser/components/loop/standalone/.gitignore
@@ -6,9 +6,9 @@ content/config.js
 content/VERSION.txt
 
 # XXX Once a grunt contrib-clean command has been added (bug 1066491), or
 # once legal has centralized their ToS and PP hosting infrastructure,
 # (expected Q4 2014) the legal doc build stuff for Loop can be removed,
 # including the following three lines:
 content/legal/styles/*.css
 content/legal/terms/*.html
-content/legal/terms/!index.html
+!content/legal/terms/index.html
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -135,17 +135,17 @@ describe("loop.panel", function() {
         TestUtils.Simulate.click(availableMenuOption);
 
         expect(view.state.showMenu).eql(true);
       });
     });
   });
 
   describe("loop.panel.PanelView", function() {
-    var fakeClient, callUrlData, view;
+    var fakeClient, callUrlData, view, callTab, contactsTab;
 
     beforeEach(function() {
       callUrlData = {
         callUrl: "http://call.invalid/",
         expiresAt: 1000
       };
 
       fakeClient = {
@@ -153,16 +153,37 @@ describe("loop.panel", function() {
           cb(null, callUrlData);
         }
       };
 
       view = TestUtils.renderIntoDocument(loop.panel.PanelView({
         notifications: notifications,
         client: fakeClient
       }));
+
+      [callTab, contactsTab] =
+        TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
+    });
+
+    describe('TabView', function() {
+      it("should select contacts tab when clicking tab button", function() {
+        TestUtils.Simulate.click(
+          view.getDOMNode().querySelector('li[data-tab-name="contacts"]'));
+
+        expect(contactsTab.getDOMNode().classList.contains("selected"))
+          .to.be.true;
+      });
+
+      it("should select call tab when clicking tab button", function() {
+        TestUtils.Simulate.click(
+          view.getDOMNode().querySelector('li[data-tab-name="call"]'));
+
+        expect(callTab.getDOMNode().classList.contains("selected"))
+          .to.be.true;
+      });
     });
 
     describe("AuthLink", function() {
       it("should trigger the FxA sign in/up process when clicking the link",
         function() {
           navigator.mozLoop.loggedInToFxA = false;
           navigator.mozLoop.logInToFxA = sandbox.stub();
 
@@ -189,28 +210,28 @@ describe("loop.panel", function() {
 
           expect(view.getDOMNode().querySelectorAll(".icon-signout"))
             .to.have.length.of(0);
           expect(view.getDOMNode().querySelectorAll(".icon-signin"))
             .to.have.length.of(1);
         });
 
       it("should show a signout entry when user is authenticated", function() {
-        navigator.mozLoop.loggedInToFxA = true;
+        navigator.mozLoop.userProfile = {email: "test@example.com"};
 
         var view = TestUtils.renderIntoDocument(loop.panel.SettingsDropdown());
 
         expect(view.getDOMNode().querySelectorAll(".icon-signout"))
           .to.have.length.of(1);
         expect(view.getDOMNode().querySelectorAll(".icon-signin"))
           .to.have.length.of(0);
       });
 
       it("should show an account entry when user is authenticated", function() {
-        navigator.mozLoop.loggedInToFxA = true;
+        navigator.mozLoop.userProfile = {email: "test@example.com"};
 
         var view = TestUtils.renderIntoDocument(loop.panel.SettingsDropdown());
 
         expect(view.getDOMNode().querySelectorAll(".icon-account"))
           .to.have.length.of(1);
       });
 
       it("should hide any account entry when user is not authenticated",
@@ -229,17 +250,17 @@ describe("loop.panel", function() {
 
         TestUtils.Simulate.click(
           view.getDOMNode().querySelector(".icon-signin"));
 
         sinon.assert.calledOnce(navigator.mozLoop.logInToFxA);
       });
 
       it("should sign out the user on click when authenticated", function() {
-        navigator.mozLoop.loggedInToFxA = true;
+        navigator.mozLoop.userProfile = {email: "test@example.com"};
         var view = TestUtils.renderIntoDocument(loop.panel.SettingsDropdown());
 
         TestUtils.Simulate.click(
           view.getDOMNode().querySelector(".icon-signout"));
 
         sinon.assert.calledOnce(navigator.mozLoop.logOutFromFxA);
       });
     });
--- a/browser/components/loop/test/mochitest/browser_fxa_login.js
+++ b/browser/components/loop/test/mochitest/browser_fxa_login.js
@@ -4,16 +4,17 @@
 /**
  * Test FxA logins with Loop.
  */
 
 "use strict";
 
 const {
   gFxAOAuthTokenData,
+  gFxAOAuthProfile,
 } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
 
 const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
 
 add_task(function* setup() {
   Services.prefs.setCharPref("loop.server", BASE_URL);
   Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
   registerCleanupFunction(function* () {
@@ -219,21 +220,32 @@ add_task(function* basicAuthorizationAnd
   info("registering");
   mockPushHandler.pushUrl = "https://localhost/pushUrl/guest";
   yield MozLoopService.register(mockPushHandler);
 
   // Normally the same pushUrl would be registered but we change it in the test
   // to be able to check for success on the second registration.
   mockPushHandler.pushUrl = "https://localhost/pushUrl/fxa";
 
+  yield loadLoopPanel({loopURL: BASE_URL, stayOnline: true});
+  let loopDoc = document.getElementById("loop").contentDocument;
+  let visibleEmail = loopDoc.getElementsByClassName("user-identity")[0];
+  is(visibleEmail.textContent, "Guest", "Guest should be displayed on the panel when not logged in");
+  is(MozLoopService.userProfile, null, "profile should be null before log-in");
+
   let tokenData = yield MozLoopService.logInToFxA();
+  yield promiseObserverNotified("loop-status-changed");
   ise(tokenData.access_token, "code1_access_token", "Check access_token");
   ise(tokenData.scope, "profile", "Check scope");
   ise(tokenData.token_type, "bearer", "Check token_type");
 
+  is(MozLoopService.userProfile.email, "test@example.com", "email should exist in the profile data");
+  is(MozLoopService.userProfile.uid, "1234abcd", "uid should exist in the profile data");
+  is(visibleEmail.textContent, "test@example.com", "the email should be correct on the panel");
+
   let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
   ise(registrationResponse.response.simplePushURL, "https://localhost/pushUrl/fxa", "Check registered push URL");
 });
 
 add_task(function* loginWithParams401() {
   resetFxA();
   let params = {
     client_id: "client_id",
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -53,27 +53,29 @@ function promiseGetMozLoopAPI() {
 }
 
 /**
  * Loads the loop panel by clicking the button and waits for its open to complete.
  * It also registers
  *
  * This assumes that the tests are running in a generatorTest.
  */
-function loadLoopPanel() {
+function loadLoopPanel(aOverrideOptions = {}) {
   // Set prefs to ensure we don't access the network externally.
-  Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
-  Services.prefs.setCharPref("loop.server", "http://localhost/");
+  Services.prefs.setCharPref("services.push.serverURL", aOverrideOptions.pushURL || "ws://localhost/");
+  Services.prefs.setCharPref("loop.server", aOverrideOptions.loopURL || "http://localhost/");
 
   // Turn off the network for loop tests, so that we don't
   // try to access the remote servers. If we want to turn this
   // back on in future, be careful to check for intermittent
   // failures.
   let wasOffline = Services.io.offline;
-  Services.io.offline = true;
+  if (!aOverrideOptions.stayOnline) {
+    Services.io.offline = true;
+  }
 
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("services.push.serverURL");
     Services.prefs.clearUserPref("loop.server");
     Services.io.offline = wasOffline;
   });
 
   // Turn off animations to make tests quicker.
@@ -98,32 +100,42 @@ function promiseOAuthParamsSetup(baseURL
 }
 
 function resetFxA() {
   let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
   global.gHawkClient = null;
   global.gFxAOAuthClientPromise = null;
   global.gFxAOAuthClient = null;
   global.gFxAOAuthTokenData = null;
+  global.gFxAOAuthProfile = null;
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   Services.prefs.clearUserPref(fxASessionPref);
 }
 
 function promiseDeletedOAuthParams(baseURL) {
   let deferred = Promise.defer();
   let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
               createInstance(Ci.nsIXMLHttpRequest);
   xhr.open("DELETE", baseURL + "/setup_params", true);
   xhr.addEventListener("load", () => deferred.resolve(xhr));
   xhr.addEventListener("error", deferred.reject);
   xhr.send();
 
   return deferred.promise;
 }
 
+function promiseObserverNotified(aTopic) {
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(onNotification, aTopic);
+      deferred.resolve({subject: aSubject, data: aData});
+    }, aTopic, false);
+  return deferred.promise;
+}
+
 /**
  * Get the last registration on the test server.
  */
 function promiseOAuthGetRegistration(baseURL) {
   let deferred = Promise.defer();
   let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
               createInstance(Ci.nsIXMLHttpRequest);
   xhr.open("GET", baseURL + "/get_registration", true);
--- a/browser/components/loop/test/mochitest/loop_fxa.sjs
+++ b/browser/components/loop/test/mochitest/loop_fxa.sjs
@@ -32,29 +32,32 @@ function handleRequest(request, response
       token(request, response);
       return;
     case "/registration":
       registration(request, response);
       return;
     case "/get_registration": // Test-only
       get_registration(request, response);
       return;
+    case "/profile/profile":
+      profile(request, response);
+      return;
   }
   response.setStatusLine(request.httpVersion, 404, "Not Found");
 }
 
 /**
  * POST /setup_params
  * DELETE /setup_params
  *
  * Test-only endpoint to setup the /fxa-oauth/params response.
  *
  * For a POST the X-Params header should contain a JSON object with keys to set for /fxa-oauth/params.
  * A DELETE request will delete the stored parameters and should be run in a cleanup function to
- * avoid interfering with subsequen tests.
+ * avoid interfering with subsequent tests.
  */
 function setup_params(request, response) {
   response.setHeader("Content-Type", "text/plain", false);
   if (request.method == "DELETE") {
     setSharedState("/fxa-oauth/params", "");
     setSharedState("/registration", "");
     response.write("Params deleted");
     return;
@@ -162,16 +165,29 @@ function token(request, response) {
     scope: "profile",
     token_type: "bearer",
   };
   response.setHeader("Content-Type", "application/json; charset=utf-8", false);
   response.write(JSON.stringify(tokenData, null, 2));
 }
 
 /**
+ * GET /profile
+ *
+ */
+function profile(request, response) {
+  response.setHeader("Content-Type", "application/json; charset=utf-8", false);
+  let profile = {
+    email: "test@example.com",
+    uid: "1234abcd",
+  };
+  response.write(JSON.stringify(profile, null, 2));
+}
+
+/**
  * POST /registration
  *
  * Mock Loop registration endpoint. Hawk Authorization headers are expected only for FxA sessions.
  */
 function registration(request, response) {
   let body = NetUtil.readInputStreamToString(request.bodyInputStream,
                                              request.bodyInputStream.available());
   let payload = JSON.parse(body);
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -121,21 +121,34 @@
           Section({name: "PanelView"}, 
             React.DOM.p({className: "note"}, 
               React.DOM.strong(null, "Note:"), " 332px wide."
             ), 
             Example({summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}}, 
               PanelView({client: mockClient, notifications: notifications, 
                          callUrl: "http://invalid.example.url/"})
             ), 
+            Example({summary: "Call URL retrieved - authenticated", dashed: "true", style: {width: "332px"}}, 
+              PanelView({client: mockClient, notifications: notifications, 
+                         callUrl: "http://invalid.example.url/",
+                         userProfile: {email: "test@example.com"}})
+            ), 
             Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}}, 
               PanelView({client: mockClient, notifications: notifications})
             ), 
+            Example({summary: "Pending call url retrieval - authenticated", dashed: "true", style: {width: "332px"}}, 
+              PanelView({client: mockClient, notifications: notifications,
+                         userProfile: {email: "test@example.com"}})
+            ), 
             Example({summary: "Error Notification", dashed: "true", style: {width: "332px"}}, 
               PanelView({client: mockClient, notifications: errNotifications})
+            ),
+            Example({summary: "Error Notification - authenticated", dashed: "true", style: {width: "332px"}}, 
+              PanelView({client: mockClient, notifications: errNotifications,
+                         userProfile: {email: "test@example.com"}})
             )
           ), 
 
           Section({name: "IncomingCallView"}, 
             Example({summary: "Default / incoming video call", dashed: "true", style: {width: "280px"}}, 
               React.DOM.div({className: "fx-embedded"}, 
                 IncomingCallView({model: mockConversationModel, 
                                   video: {enabled: true}})
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -121,22 +121,35 @@
           <Section name="PanelView">
             <p className="note">
               <strong>Note:</strong> 332px wide.
             </p>
             <Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
               <PanelView client={mockClient} notifications={notifications}
                          callUrl="http://invalid.example.url/" />
             </Example>
+            <Example summary="Call URL retrieved - authenticated" dashed="true" style={{width: "332px"}}>
+              <PanelView client={mockClient} notifications={notifications}
+                         callUrl="http://invalid.example.url/"
+                         userProfile={{email: "test@example.com"}} />
+            </Example>
             <Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
               <PanelView client={mockClient} notifications={notifications} />
             </Example>
+            <Example summary="Pending call url retrieval - authenticated" dashed="true" style={{width: "332px"}}>
+              <PanelView client={mockClient} notifications={notifications}
+                         userProfile={{email: "test@example.com"}} />
+            </Example>
             <Example summary="Error Notification" dashed="true" style={{width: "332px"}}>
               <PanelView client={mockClient} notifications={errNotifications}/>
             </Example>
+            <Example summary="Error Notification - authenticated" dashed="true" style={{width: "332px"}}>
+              <PanelView client={mockClient} notifications={errNotifications}
+                         userProfile={{email: "test@example.com"}} />
+            </Example>
           </Section>
 
           <Section name="IncomingCallView">
             <Example summary="Default / incoming video call" dashed="true" style={{width: "280px"}}>
               <div className="fx-embedded">
                 <IncomingCallView model={mockConversationModel}
                                   video={{enabled: true}} />
               </div>
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -144,17 +144,19 @@
                      type="autocomplete"
                      class="padded"
                      flex="1"
                      autocompletesearch="places-tag-autocomplete" 
                      completedefaultindex="true"
                      tabscrolling="true"
                      showcommentcolumn="true"
                      observes="paneElementsBroadcaster"
-                     placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"/>
+                     placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"
+                     maxlength="1000"
+                     />
             <button id="editBMPanel_tagsSelectorExpander"
                     class="expander-down"
                     tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                     tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                     tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
                     oncommand="gEditItemOverlay.toggleTagsSelector();"
                     observes="paneElementsBroadcaster"/>
           </hbox>
--- a/browser/components/places/content/moveBookmarks.js
+++ b/browser/components/places/content/moveBookmarks.js
@@ -46,18 +46,18 @@ var gMoveBookmarksDialog = {
     }
 
     PlacesTransactions.transact(function* () {
       let newParentGUID = yield PlacesUtils.promiseItemGUID(selectedFolderId);
       for (let node of this._nodes) {
         // Nothing to do if the node is already under the selected folder.
         if (node.parent.itemId == selectedFolderId)
           continue;
-        yield PlacesTransactions.MoveItem({ GUID: node.bookmarkGuid
-                                          , newParentGUID: newParentGUID });
+        yield PlacesTransactions.Move({ GUID: node.bookmarkGuid
+                                      , newParentGUID: newParentGUID });
       }
     }.bind(this)).then(null, Components.utils.reportError);
   },
 
   newFolder: function MBD_newFolder() {
     // The command is disabled when the tree is not focused
     this.foldersTree.focus();
     goDoCommand("placesCmd_new:folder");
--- a/browser/themes/shared/badcontent-doorhanger.inc.css
+++ b/browser/themes/shared/badcontent-doorhanger.inc.css
@@ -1,16 +1,21 @@
 .popup-notification-item-title[popupid="bad-content"] {
   font-weight: bold;
 }
 
 .popup-notification-item-message[popupid="bad-content"] {
   width: 17em;
 }
 
+.popup-notification-item-message[popupid="bad-content"][mixedblockdisabled]:not(.popup-notification-item-message-critical),
+.popup-notification-item-message[popupid="bad-content"][trackingblockdisabled]:not(.popup-notification-item-message-critical) {
+  color: GrayText;
+}
+
 .popup-notification-item-message-critical[popupid="bad-content"] {
   color: #d74345;
   font-style: italic;
 }
 
 .popup-notification-footer[popupid="bad-content"] {
   padding-top: 1em;
 }
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -18,16 +18,26 @@ prefwindow,
 prefpane {
   max-width: 800px;
   padding: 0;
   font: message-box;
   font-size: 1.25rem;
   line-height: 22px;
 }
 
+* {
+  -moz-user-select: text;
+}
+
+button,
+treecol {
+  /* override the * rule */
+  -moz-user-select: none;
+}
+
 /* Category List */
 
 .category-icon {
   list-style-image: url("chrome://browser/skin/preferences/in-content/icons.png");
 }
 
 #category-general > .category-icon {
   -moz-image-region: rect(0, 24px, 24px, 0);
--- a/content/media/CubebUtils.cpp
+++ b/content/media/CubebUtils.cpp
@@ -1,50 +1,37 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 <stdint.h>
 #include <algorithm>
-#include "mozilla/Atomics.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/StaticMutex.h"
 #include "CubebUtils.h"
-#include "nsAutoRef.h"
 #include "prdtoa.h"
 
 #define PREF_VOLUME_SCALE "media.volume_scale"
 #define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
 
 namespace mozilla {
 
-namespace {
-
-// Prefered samplerate, in Hz (characteristic of the
-// hardware/mixer/platform/API used).
-Atomic<uint32_t> sPreferredSampleRate;
-
-// This mutex protects the variables below.
-StaticMutex sMutex;
-cubeb* sCubebContext;
-double sVolumeScale;
-uint32_t sCubebLatency;
-bool sCubebLatencyPrefSet;
-
-} // anonymous namespace
-
 extern PRLogModuleInfo* gAudioStreamLog;
 
 static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
 
-namespace CubebUtils {
+StaticMutex CubebUtils::sMutex;
+cubeb* CubebUtils::sCubebContext;
+uint32_t CubebUtils::sPreferredSampleRate;
+double CubebUtils::sVolumeScale;
+uint32_t CubebUtils::sCubebLatency;
+bool CubebUtils::sCubebLatencyPrefSet;
 
-void PrefChanged(const char* aPref, void* aClosure)
+/*static*/ void CubebUtils::PrefChanged(const char* aPref, void* aClosure)
 {
   if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
     nsAdoptingString value = Preferences::GetString(aPref);
     StaticMutexAutoLock lock(sMutex);
     if (value.IsEmpty()) {
       sVolumeScale = 1.0;
     } else {
       NS_ConvertUTF16toUTF8 utf8(value);
@@ -56,123 +43,116 @@ void PrefChanged(const char* aPref, void
     // audible.
     sCubebLatencyPrefSet = Preferences::HasUserValue(aPref);
     uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
     StaticMutexAutoLock lock(sMutex);
     sCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
   }
 }
 
-bool GetFirstStream()
+/*static*/ bool CubebUtils::GetFirstStream()
 {
   static bool sFirstStream = true;
 
   StaticMutexAutoLock lock(sMutex);
   bool result = sFirstStream;
   sFirstStream = false;
   return result;
 }
 
-double GetVolumeScale()
+/*static*/ double CubebUtils::GetVolumeScale()
 {
   StaticMutexAutoLock lock(sMutex);
   return sVolumeScale;
 }
 
-cubeb* GetCubebContext()
+/*static*/ cubeb* CubebUtils::GetCubebContext()
 {
   StaticMutexAutoLock lock(sMutex);
   return GetCubebContextUnlocked();
 }
 
-void InitPreferredSampleRate()
+/*static*/ void CubebUtils::InitPreferredSampleRate()
 {
-  // The mutex is used here to prohibit concurrent initialization calls, but
-  // sPreferredSampleRate itself is safe to access without the mutex because
-  // it is using atomic storage.
   StaticMutexAutoLock lock(sMutex);
-  uint32_t preferredSampleRate = 0;
   if (sPreferredSampleRate == 0 &&
       cubeb_get_preferred_sample_rate(GetCubebContextUnlocked(),
-                                      &preferredSampleRate) == CUBEB_OK) {
-    sPreferredSampleRate = preferredSampleRate;
-  } else {
-    // Query failed, use a sensible default.
+                                      &sPreferredSampleRate) != CUBEB_OK) {
     sPreferredSampleRate = 44100;
   }
 }
 
-cubeb* GetCubebContextUnlocked()
+/*static*/ cubeb* CubebUtils::GetCubebContextUnlocked()
 {
   sMutex.AssertCurrentThreadOwns();
   if (sCubebContext ||
       cubeb_init(&sCubebContext, "CubebUtils") == CUBEB_OK) {
     return sCubebContext;
   }
   NS_WARNING("cubeb_init failed");
   return nullptr;
 }
 
-uint32_t GetCubebLatency()
+/*static*/ uint32_t CubebUtils::GetCubebLatency()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatency;
 }
 
-bool CubebLatencyPrefSet()
+/*static*/ bool CubebUtils::CubebLatencyPrefSet()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatencyPrefSet;
 }
 
-void InitLibrary()
+/*static*/ void CubebUtils::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 }
 
-void ShutdownLibrary()
+/*static*/ void CubebUtils::ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 
   StaticMutexAutoLock lock(sMutex);
   if (sCubebContext) {
     cubeb_destroy(sCubebContext);
     sCubebContext = nullptr;
   }
 }
 
-uint32_t MaxNumberOfChannels()
+/*static*/ int CubebUtils::MaxNumberOfChannels()
 {
-  cubeb* cubebContext = GetCubebContext();
+  cubeb* cubebContext = CubebUtils::GetCubebContext();
   uint32_t maxNumberOfChannels;
   if (cubebContext &&
       cubeb_get_max_channel_count(cubebContext,
                                   &maxNumberOfChannels) == CUBEB_OK) {
-    return maxNumberOfChannels;
+    return static_cast<int>(maxNumberOfChannels);
   }
 
   return 0;
 }
 
-uint32_t PreferredSampleRate()
+/*static*/ int CubebUtils::PreferredSampleRate()
 {
   MOZ_ASSERT(sPreferredSampleRate,
              "sPreferredSampleRate has not been initialized!");
   return sPreferredSampleRate;
 }
 
 #if defined(__ANDROID__) && defined(MOZ_B2G)
-cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel)
+/*static*/ cubeb_stream_type CubebUtils::ConvertChannelToCubebType(dom::AudioChannel aChannel)
 {
   switch(aChannel) {
     case dom::AudioChannel::Normal:
       return CUBEB_STREAM_TYPE_SYSTEM;
     case dom::AudioChannel::Content:
       return CUBEB_STREAM_TYPE_MUSIC;
     case dom::AudioChannel::Notification:
       return CUBEB_STREAM_TYPE_NOTIFICATION;
@@ -186,10 +166,9 @@ cubeb_stream_type ConvertChannelToCubebT
       return CUBEB_STREAM_TYPE_SYSTEM_ENFORCED;
     default:
       NS_ERROR("The value of AudioChannel is invalid");
       return CUBEB_STREAM_TYPE_MAX;
   }
 }
 #endif
 
-} // namespace CubebUtils
-} // namespace mozilla
+}
--- a/content/media/CubebUtils.h
+++ b/content/media/CubebUtils.h
@@ -3,47 +3,70 @@
 /* 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/. */
 
 #if !defined(CubebUtils_h_)
 #define CubebUtils_h_
 
 #include "cubeb/cubeb.h"
+#include "nsAutoRef.h"
+#include "mozilla/StaticMutex.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 
+template <>
+class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
+{
+public:
+  static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
+};
+
 namespace mozilla {
-namespace CubebUtils {
 
-// Initialize Audio Library. Some Audio backends require initializing the
-// library before using it.
-void InitLibrary();
+class CubebUtils {
+public:
+  // Initialize Audio Library. Some Audio backends require initializing the
+  // library before using it.
+  static void InitLibrary();
 
-// Shutdown Audio Library. Some Audio backends require shutting down the
-// library after using it.
-void ShutdownLibrary();
+  // Shutdown Audio Library. Some Audio backends require shutting down the
+  // library after using it.
+  static void ShutdownLibrary();
 
-// Returns the maximum number of channels supported by the audio hardware.
-uint32_t MaxNumberOfChannels();
+  // Returns the maximum number of channels supported by the audio hardware.
+  static int MaxNumberOfChannels();
 
-// Queries the samplerate the hardware/mixer runs at, and stores it.
-// Can be called on any thread. When this returns, it is safe to call
-// PreferredSampleRate.
-void InitPreferredSampleRate();
-
-// Get the aforementioned sample rate. Thread safe.
-uint32_t PreferredSampleRate();
+  // Queries the samplerate the hardware/mixer runs at, and stores it.
+  // Can be called on any thread. When this returns, it is safe to call
+  // PreferredSampleRate without locking.
+  static void InitPreferredSampleRate();
+  // Get the aformentionned sample rate. Does not lock.
+  static int PreferredSampleRate();
 
-void PrefChanged(const char* aPref, void* aClosure);
-double GetVolumeScale();
-bool GetFirstStream();
-cubeb* GetCubebContext();
-cubeb* GetCubebContextUnlocked();
-uint32_t GetCubebLatency();
-bool CubebLatencyPrefSet();
+  static void PrefChanged(const char* aPref, void* aClosure);
+  static double GetVolumeScale();
+  static bool GetFirstStream();
+  static cubeb* GetCubebContext();
+  static cubeb* GetCubebContextUnlocked();
+  static uint32_t GetCubebLatency();
+  static bool CubebLatencyPrefSet();
 #if defined(__ANDROID__) && defined(MOZ_B2G)
-cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
+  static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
 #endif
 
-} // namespace CubebUtils
-} // namespace mozilla
+private:
+  // This mutex protects the static members below.
+  static StaticMutex sMutex;
+  static cubeb* sCubebContext;
+
+  // Prefered samplerate, in Hz (characteristic of the
+  // hardware/mixer/platform/API used).
+  static uint32_t sPreferredSampleRate;
+
+  static double sVolumeScale;
+  static uint32_t sCubebLatency;
+  static bool sCubebLatencyPrefSet;
+};
+}
+
+
 
 #endif // CubebUtils_h_
--- a/content/media/GraphDriver.h
+++ b/content/media/GraphDriver.h
@@ -9,24 +9,18 @@
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "AudioBufferUtils.h"
 #include "AudioMixer.h"
 #include "AudioSegment.h"
 
 struct cubeb_stream;
 
-template <>
-class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
-{
-public:
-  static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
-};
+namespace mozilla {
 
-namespace mozilla {
 
 /**
  * Assume we can run an iteration of the MediaStreamGraph loop in this much time
  * or less.
  * We try to run the control loop at this rate.
  */
 static const int MEDIA_GRAPH_TARGET_PERIOD_MS = 10;
 
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -2,21 +2,19 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "BluetoothRilListener.h"
 
 #include "BluetoothHfpManager.h"
-#include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionService.h"
-#include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h" // For NS_RILCONTENTHELPER_CONTRACTID.
+#include "nsIRadioInterfaceLayer.h"
+#include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 /**
  *  IccListener
  */
@@ -176,25 +174,21 @@ MobileConnectionListener::NotifyNetworkS
 
 bool
 MobileConnectionListener::Listen(bool aStart)
 {
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(service, false);
 
-  nsCOMPtr<nsIMobileConnection> connection;
-  service->GetItemByServiceId(mClientId, getter_AddRefs(connection));
-  NS_ENSURE_TRUE(connection, false);
-
   nsresult rv;
   if (aStart) {
-    rv = connection->RegisterListener(this);
+    rv = service->RegisterListener(mClientId, this);
   } else {
-    rv = connection->UnregisterListener(this);
+    rv = service->UnregisterListener(mClientId, this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  TelephonyListener Implementation
  */
@@ -330,27 +324,27 @@ TelephonyListener::Listen(bool aStart)
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  BluetoothRilListener
  */
 BluetoothRilListener::BluetoothRilListener()
 {
-  nsCOMPtr<nsIMobileConnectionService> service =
-    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(service);
+  // Query number of total clients (sim slots)
+  uint32_t numOfClients;
+  nsCOMPtr<nsIRadioInterfaceLayer> radioInterfaceLayer =
+    do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(radioInterfaceLayer);
 
-  // Query number of total clients (sim slots)
-  uint32_t numItems = 0;
-  if (NS_SUCCEEDED(service->GetNumItems(&numItems))) {
-    // Init MobileConnectionListener array and IccInfoListener
-    for (uint32_t i = 0; i < numItems; i++) {
-      mMobileConnListeners.AppendElement(new MobileConnectionListener(i));
-    }
+  radioInterfaceLayer->GetNumRadioInterfaces(&numOfClients);
+
+  // Init MobileConnectionListener array and IccInfoListener
+  for (uint32_t i = 0; i < numOfClients; i++) {
+    mMobileConnListeners.AppendElement(new MobileConnectionListener(i));
   }
 
   mTelephonyListener = new TelephonyListener();
   mIccListener = new IccListener();
   mIccListener->SetOwner(this);
 
   // Probe for available client
   SelectClient();
@@ -376,25 +370,18 @@ BluetoothRilListener::SelectClient()
   // Reset mClientId
   mClientId = mMobileConnListeners.Length();
 
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(service);
 
   for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
-    nsCOMPtr<nsIMobileConnection> connection;
-    service->GetItemByServiceId(i, getter_AddRefs(connection));
-    if (!connection) {
-      BT_WARNING("%s: Failed to get mobile connection", __FUNCTION__);
-      continue;
-    }
-
     nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-    connection->GetVoice(getter_AddRefs(voiceInfo));
+    service->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
     if (!voiceInfo) {
       BT_WARNING("%s: Failed to get voice connection info", __FUNCTION__);
       continue;
     }
 
     nsString regState;
     voiceInfo->GetState(regState);
     if (regState.EqualsLiteral("registered")) {
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -607,26 +607,22 @@ BluetoothHfpManager::HandleVolumeChanged
     sBluetoothHfpInterface->VolumeControl(HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs,
                                           new VolumeControlResultHandler());
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionService> mcService =
+  nsCOMPtr<nsIMobileConnectionService> connection =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(mcService);
-
-  nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(aClientId, getter_AddRefs(connection));
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-  connection->GetVoice(getter_AddRefs(voiceInfo));
+  connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   // Roam
   bool roaming;
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -601,26 +601,22 @@ BluetoothHfpManager::HandleVolumeChanged
     SendCommand(RESPONSE_VGS, mCurrentVgs);
   }
 }
 
 #ifdef MOZ_B2G_RIL
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionService> mcService =
+  nsCOMPtr<nsIMobileConnectionService> connection =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(mcService);
-
-  nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(aClientId, getter_AddRefs(connection));
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-  connection->GetVoice(getter_AddRefs(voiceInfo));
+  connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   bool roaming;
   voiceInfo->GetRoaming(&roaming);
@@ -645,17 +641,17 @@ BluetoothHfpManager::HandleVoiceConnecti
 
   /**
    * Possible return values for mode are:
    * - null (unknown): set mNetworkSelectionMode to 0 (auto)
    * - automatic: set mNetworkSelectionMode to 0 (auto)
    * - manual: set mNetworkSelectionMode to 1 (manual)
    */
   nsString mode;
-  connection->GetNetworkSelectionMode(mode);
+  connection->GetNetworkSelectionMode(aClientId, mode);
   if (mode.EqualsLiteral("manual")) {
     mNetworkSelectionMode = 1;
   } else {
     mNetworkSelectionMode = 0;
   }
 
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
--- a/dom/bluetooth2/BluetoothRilListener.cpp
+++ b/dom/bluetooth2/BluetoothRilListener.cpp
@@ -2,21 +2,19 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "BluetoothRilListener.h"
 
 #include "BluetoothHfpManager.h"
-#include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionService.h"
-#include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h" // For NS_RILCONTENTHELPER_CONTRACTID.
+#include "nsIRadioInterfaceLayer.h"
+#include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 /**
  *  IccListener
  */
@@ -176,25 +174,21 @@ MobileConnectionListener::NotifyNetworkS
 
 bool
 MobileConnectionListener::Listen(bool aStart)
 {
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(service, false);
 
-  nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(mClientId, getter_AddRefs(connection));
-  NS_ENSURE_TRUE(connection, false);
-
   nsresult rv;
   if (aStart) {
-    rv = connection->RegisterListener(this);
+    rv = service->RegisterListener(mClientId, this);
   } else {
-    rv = connection->UnregisterListener(this);
+    rv = service->UnregisterListener(mClientId, this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  TelephonyListener Implementation
  */
@@ -330,27 +324,27 @@ TelephonyListener::Listen(bool aStart)
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  BluetoothRilListener
  */
 BluetoothRilListener::BluetoothRilListener()
 {
-  nsCOMPtr<nsIMobileConnectionService> service =
-    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(service);
+  // Query number of total clients (sim slots)
+  uint32_t numOfClients;
+  nsCOMPtr<nsIRadioInterfaceLayer> radioInterfaceLayer =
+    do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(radioInterfaceLayer);
 
-  // Query number of total clients (sim slots)
-  uint32_t numItems = 0;
-  if (NS_SUCCEEDED(service->GetNumItems(&numItems))) {
-    // Init MobileConnectionListener array and IccInfoListener
-    for (uint32_t i = 0; i < numItems; i++) {
-      mMobileConnListeners.AppendElement(new MobileConnectionListener(i));
-    }
+  radioInterfaceLayer->GetNumRadioInterfaces(&numOfClients);
+
+  // Init MobileConnectionListener array and IccInfoListener
+  for (uint32_t i = 0; i < numOfClients; i++) {
+    mMobileConnListeners.AppendElement(new MobileConnectionListener(i));
   }
 
   mTelephonyListener = new TelephonyListener();
   mIccListener = new IccListener();
   mIccListener->SetOwner(this);
 
   // Probe for available client
   SelectClient();
@@ -376,25 +370,18 @@ BluetoothRilListener::SelectClient()
   // Reset mClientId
   mClientId = mMobileConnListeners.Length();
 
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(service);
 
   for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
-    nsCOMPtr<nsIMobileConnection> connection;
-    service->GetItemByServiceId(i, getter_AddRefs(connection));
-    if (!connection) {
-      BT_WARNING("%s: Failed to get mobile connection", __FUNCTION__);
-      continue;
-    }
-
     nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-    connection->GetVoice(getter_AddRefs(voiceInfo));
+    service->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
     if (!voiceInfo) {
       BT_WARNING("%s: Failed to get voice connection info", __FUNCTION__);
       continue;
     }
 
     nsString regState;
     voiceInfo->GetState(regState);
     if (regState.EqualsLiteral("registered")) {
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -610,26 +610,22 @@ BluetoothHfpManager::HandleVolumeChanged
     sBluetoothHfpInterface->VolumeControl(HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs,
                                           new VolumeControlResultHandler());
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionService> mcService =
+  nsCOMPtr<nsIMobileConnectionService> connection =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(mcService);
-
-  nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(aClientId, getter_AddRefs(connection));
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-  connection->GetVoice(getter_AddRefs(voiceInfo));
+  connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   // Roam
   bool roaming;
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -601,26 +601,22 @@ BluetoothHfpManager::HandleVolumeChanged
     SendCommand(RESPONSE_VGS, mCurrentVgs);
   }
 }
 
 #ifdef MOZ_B2G_RIL
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionService> mcService =
+  nsCOMPtr<nsIMobileConnectionService> connection =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(mcService);
-
-  nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(aClientId, getter_AddRefs(connection));
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-  connection->GetVoice(getter_AddRefs(voiceInfo));
+  connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   bool roaming;
   voiceInfo->GetRoaming(&roaming);
@@ -645,17 +641,17 @@ BluetoothHfpManager::HandleVoiceConnecti
 
   /**
    * Possible return values for mode are:
    * - null (unknown): set mNetworkSelectionMode to 0 (auto)
    * - automatic: set mNetworkSelectionMode to 0 (auto)
    * - manual: set mNetworkSelectionMode to 1 (manual)
    */
   nsString mode;
-  connection->GetNetworkSelectionMode(mode);
+  connection->GetNetworkSelectionMode(aClientId, mode);
   if (mode.EqualsLiteral("manual")) {
     mNetworkSelectionMode = 1;
   } else {
     mNetworkSelectionMode = 0;
   }
 
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
--- a/dom/interfaces/notification/nsINotificationStorage.idl
+++ b/dom/interfaces/notification/nsINotificationStorage.idl
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(9b12f566-2c7f-48ef-990d-e5092a71d11e)]
+[scriptable, uuid(9f1c43b9-f01b-4c87-ad3d-1a86520c2159)]
 interface nsINotificationStorageCallback : nsISupports
 {
   /**
    * Callback function used to pass single notification back
    * into C++ land for Notification.get return data.
    *
    * @param id: a uuid for this notification
    * @param title: the notification title
@@ -22,30 +22,31 @@ interface nsINotificationStorageCallback
   [implicit_jscontext]
   void handle(in DOMString id,
               in DOMString title,
               in DOMString dir,
               in DOMString lang,
               in DOMString body,
               in DOMString tag,
               in DOMString icon,
-              in DOMString data);
+              in DOMString data,
+              in DOMString behavior);
 
   /**
    * Callback function used to notify C++ the we have returned
    * all notification objects for this Notification.get call.
    */
   [implicit_jscontext]
   void done();
 };
 
 /**
  * Interface for notification persistence layer.
  */
-[scriptable, uuid(1be733d9-d614-43f2-9fd4-8f573a33b215)]
+[scriptable, uuid(f5145be6-e34b-468b-84da-c8c4c1ad60fe)]
 interface nsINotificationStorage : nsISupports
 {
 
   /**
    * Add/replace a notification to the persistence layer.
    *
    * @param origin: the origin/app of this notification
    * @param id: a uuid for this notification
@@ -65,17 +66,18 @@ interface nsINotificationStorage : nsISu
            in DOMString id,
            in DOMString title,
            in DOMString dir,
            in DOMString lang,
            in DOMString body,
            in DOMString tag,
            in DOMString icon,
            in DOMString alertName,
-           in DOMString data);
+           in DOMString data,
+           in DOMString behavior);
 
   /**
    * Retrieve a list of notifications.
    *
    * @param origin: the origin/app for which to fetch notifications from
    * @param tag: used to fetch only a specific tag
    * @param callback: nsINotificationStorageCallback, used for
    *                  returning notifications objects
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -121,17 +121,16 @@
 #include "nsIAccessibilityService.h"
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
 #include <setjmp.h>
 #include "ipc/Nuwa.h"
 #endif
 
-#include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
@@ -150,23 +149,27 @@
 #include "AudioChannelService.h"
 #include "JavaScriptChild.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 
+#ifdef MOZ_B2G_RIL
+#include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
+using namespace mozilla::dom::mobileconnection;
+#endif
+
 using namespace base;
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
-using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 #if defined(MOZ_WIDGET_GONK)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -39,21 +39,20 @@
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
-#include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
-#include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
+#include "SmsParent.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
@@ -173,31 +172,34 @@ using namespace mozilla::system;
 #endif
 
 #ifdef ENABLE_TESTS
 #include "BackgroundChildImpl.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #endif
 
+#ifdef MOZ_B2G_RIL
+#include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
+using namespace mozilla::dom::mobileconnection;
+#endif
 
 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #endif
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
-using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::widget;
--- a/dom/mobileconnection/DOMMMIError.cpp
+++ b/dom/mobileconnection/DOMMMIError.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/DOMMMIError.h"
+#include "DOMMMIError.h"
 #include "mozilla/dom/DOMMMIErrorBinding.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMMIError)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMMIError, DOMError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
--- a/dom/mobileconnection/MobileCellInfo.cpp
+++ b/dom/mobileconnection/MobileCellInfo.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/MobileCellInfo.h"
+#include "MobileCellInfo.h"
 #include "mozilla/dom/MozMobileCellInfoBinding.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MobileCellInfo, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MobileCellInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MobileCellInfo)
--- a/dom/mobileconnection/MobileConnection.cpp
+++ b/dom/mobileconnection/MobileConnection.cpp
@@ -1,13 +1,13 @@
 /* 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 "mozilla/dom/MobileConnection.h"
+#include "MobileConnection.h"
 
 #include "MobileConnectionCallback.h"
 #include "mozilla/dom/CFStateChangeEvent.h"
 #include "mozilla/dom/DataErrorEvent.h"
 #include "mozilla/dom/MozClirModeEvent.h"
 #include "mozilla/dom/MozEmergencyCbModeEvent.h"
 #include "mozilla/dom/MozOtaStatusEvent.h"
 #include "mozilla/dom/ToJSValue.h"
@@ -37,17 +37,16 @@
 {                                                                       \
   uint32_t index = uint32_t(_enum);                                     \
   _string.AssignASCII(_enumType##Values::strings[index].value,          \
                       _enumType##Values::strings[index].length);        \
 }
 
 using mozilla::ErrorResult;
 using namespace mozilla::dom;
-using namespace mozilla::dom::mobileconnection;
 
 class MobileConnection::Listener MOZ_FINAL : public nsIMobileConnectionListener
 {
   MobileConnection* mMobileConnection;
 
 public:
   NS_DECL_ISUPPORTS
   NS_FORWARD_SAFE_NSIMOBILECONNECTIONLISTENER(mMobileConnection)
@@ -73,19 +72,18 @@ private:
 
 NS_IMPL_ISUPPORTS(MobileConnection::Listener, nsIMobileConnectionListener)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MobileConnection)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MobileConnection,
                                                   DOMEventTargetHelper)
   // Don't traverse mListener because it doesn't keep any reference to
-  // MobileConnection but a raw pointer instead. Neither does mMobileConnection
-  // because it's an xpcom service owned object and is only released at shutting
-  // down.
+  // MobileConnection but a raw pointer instead. Neither does mService because
+  // it's an xpcom service and is only released at shutting down.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoice)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MobileConnection,
                                                 DOMEventTargetHelper)
   tmp->Shutdown();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVoice)
@@ -98,55 +96,48 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   // to us.
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MobileConnection, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MobileConnection, DOMEventTargetHelper)
 
 MobileConnection::MobileConnection(nsPIDOMWindow* aWindow, uint32_t aClientId)
   : DOMEventTargetHelper(aWindow)
+  , mClientId(aClientId)
 {
   SetIsDOMBinding();
 
-  nsCOMPtr<nsIMobileConnectionService> service =
-    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  mService = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
 
   // Not being able to acquire the service isn't fatal since we check
   // for it explicitly below.
-  if (!service) {
+  if (!mService) {
     NS_WARNING("Could not acquire nsIMobileConnectionService!");
     return;
   }
 
-  nsresult rv = service->GetItemByServiceId(aClientId,
-                                            getter_AddRefs(mMobileConnection));
-  if (NS_FAILED(rv) || !mMobileConnection) {
-    NS_WARNING("Could not acquire nsIMobileConnection!");
-    return;
-  }
-
   mListener = new Listener(this);
   mVoice = new MobileConnectionInfo(GetOwner());
   mData = new MobileConnectionInfo(GetOwner());
 
   if (CheckPermission("mobileconnection")) {
-    DebugOnly<nsresult> rv = mMobileConnection->RegisterListener(mListener);
+    DebugOnly<nsresult> rv = mService->RegisterListener(mClientId, mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Failed registering mobile connection messages with service");
     UpdateVoice();
     UpdateData();
   }
 }
 
 void
 MobileConnection::Shutdown()
 {
   if (mListener) {
-    if (mMobileConnection) {
-      mMobileConnection->UnregisterListener(mListener);
+    if (mService) {
+      mService->UnregisterListener(mClientId, mListener);
     }
 
     mListener->Disconnect();
     mListener = nullptr;
   }
 }
 
 MobileConnection::~MobileConnection()
@@ -179,61 +170,61 @@ MobileConnection::CheckPermission(const 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
   permMgr->TestPermissionFromWindow(GetOwner(), aType, &permission);
   return permission == nsIPermissionManager::ALLOW_ACTION;
 }
 
 void
 MobileConnection::UpdateVoice()
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  mMobileConnection->GetVoice(getter_AddRefs(info));
+  mService->GetVoiceConnectionInfo(mClientId, getter_AddRefs(info));
   mVoice->Update(info);
 }
 
 void
 MobileConnection::UpdateData()
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  mMobileConnection->GetData(getter_AddRefs(info));
+  mService->GetDataConnectionInfo(mClientId, getter_AddRefs(info));
   mData->Update(info);
 }
 
 // WebIDL interface
 
 void
 MobileConnection::GetLastKnownNetwork(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
-  mMobileConnection->GetLastKnownNetwork(aRetVal);
+  mService->GetLastKnownNetwork(mClientId, aRetVal);
 }
 
 void
 MobileConnection::GetLastKnownHomeNetwork(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
-  mMobileConnection->GetLastKnownHomeNetwork(aRetVal);
+  mService->GetLastKnownHomeNetwork(mClientId, aRetVal);
 }
 
 // All fields below require the "mobileconnection" permission.
 
 MobileConnectionInfo*
 MobileConnection::Voice() const
 {
   return mVoice;
@@ -245,350 +236,362 @@ MobileConnection::Data() const
   return mData;
 }
 
 void
 MobileConnection::GetIccId(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
-  mMobileConnection->GetIccId(aRetVal);
+  mService->GetIccId(mClientId, aRetVal);
 }
 
 Nullable<MobileNetworkSelectionMode>
 MobileConnection::GetNetworkSelectionMode() const
 {
   Nullable<MobileNetworkSelectionMode> retVal =
     Nullable<MobileNetworkSelectionMode>();
 
-  if (!mMobileConnection) {
+  if (!mService) {
     return retVal;
   }
 
   nsAutoString mode;
-  mMobileConnection->GetNetworkSelectionMode(mode);
+  mService->GetNetworkSelectionMode(mClientId, mode);
   CONVERT_STRING_TO_NULLABLE_ENUM(mode, MobileNetworkSelectionMode, retVal);
 
   return retVal;
 }
 
 Nullable<MobileRadioState>
 MobileConnection::GetRadioState() const
 {
   Nullable<MobileRadioState> retVal = Nullable<MobileRadioState>();
 
-  if (!mMobileConnection) {
+  if (!mService) {
     return retVal;
   }
 
   nsAutoString state;
-  mMobileConnection->GetRadioState(state);
+  mService->GetRadioState(mClientId, state);
   CONVERT_STRING_TO_NULLABLE_ENUM(state, MobileRadioState, retVal);
 
   return retVal;
 }
 
 void
 MobileConnection::GetSupportedNetworkTypes(nsTArray<MobileNetworkType>& aTypes) const
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     return;
   }
 
-  char16_t** types = nullptr;
-  uint32_t length = 0;
+  nsCOMPtr<nsIVariant> variant;
+  mService->GetSupportedNetworkTypes(mClientId,
+                                     getter_AddRefs(variant));
 
-  nsresult rv = mMobileConnection->GetSupportedNetworkTypes(&types, &length);
-  NS_ENSURE_SUCCESS_VOID(rv);
+  uint16_t type;
+  nsIID iid;
+  uint32_t count;
+  void* data;
 
-  for (uint32_t i = 0; i < length; ++i) {
-    nsDependentString rawType(types[i]);
-    Nullable<MobileNetworkType> type = Nullable<MobileNetworkType>();
-    CONVERT_STRING_TO_NULLABLE_ENUM(rawType, MobileNetworkType, type);
+  // Convert the nsIVariant to an array.  We own the resulting buffer and its
+  // elements.
+  if (NS_FAILED(variant->GetAsArray(&type, &iid, &count, &data))) {
+    return;
+  }
 
-    if (!type.IsNull()) {
-      aTypes.AppendElement(type.Value());
+  // We expect the element type is wstring.
+  if (type == nsIDataType::VTYPE_WCHAR_STR) {
+    char16_t** rawArray = reinterpret_cast<char16_t**>(data);
+    for (uint32_t i = 0; i < count; ++i) {
+      nsDependentString rawType(rawArray[i]);
+      Nullable<MobileNetworkType> type = Nullable<MobileNetworkType>();
+      CONVERT_STRING_TO_NULLABLE_ENUM(rawType, MobileNetworkType, type);
+
+      if (!type.IsNull()) {
+        aTypes.AppendElement(type.Value());
+      }
     }
   }
-
-  NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, types);
+  NS_Free(data);
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetNetworks(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetNetworks(requestCallback);
+  nsresult rv = mService->GetNetworks(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SelectNetwork(MobileNetworkInfo& aNetwork, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SelectNetwork(&aNetwork, requestCallback);
+  nsresult rv = mService->SelectNetwork(mClientId, &aNetwork, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SelectNetworkAutomatically(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->SelectNetworkAutomatically(requestCallback);
+  nsresult rv = mService->SelectNetworkAutomatically(mClientId,
+                                                     requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetPreferredNetworkType(MobilePreferredNetworkType& aType,
                                           ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsAutoString type;
   CONVERT_ENUM_TO_STRING(MobilePreferredNetworkType, aType, type);
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->SetPreferredNetworkType(type, requestCallback);
+  nsresult rv = mService->SetPreferredNetworkType(mClientId, type,
+                                                  requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetPreferredNetworkType(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetPreferredNetworkType(requestCallback);
+  nsresult rv = mService->GetPreferredNetworkType(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetRoamingPreference(MobileRoamingMode& aMode,
                                        ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsAutoString mode;
   CONVERT_ENUM_TO_STRING(MobileRoamingMode, aMode, mode);
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SetRoamingPreference(mode, requestCallback);
+  nsresult rv = mService->SetRoamingPreference(mClientId, mode, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetRoamingPreference(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetRoamingPreference(requestCallback);
+  nsresult rv = mService->GetRoamingPreference(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetVoicePrivacyMode(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->SetVoicePrivacyMode(aEnabled, requestCallback);
+  nsresult rv = mService->SetVoicePrivacyMode(mClientId, aEnabled,
+                                              requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetVoicePrivacyMode(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetVoicePrivacyMode(requestCallback);
+  nsresult rv = mService->GetVoicePrivacyMode(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SendMMI(const nsAString& aMMIString, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SendMMI(aMMIString, requestCallback);
+  nsresult rv = mService->SendMMI(mClientId, aMMIString, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::CancelMMI(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->CancelMMI(requestCallback);
+  nsresult rv = mService->CancelMMI(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallForwardingOption(uint16_t aReason, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetCallForwarding(aReason, requestCallback);
+  nsresult rv = mService->GetCallForwarding(mClientId, aReason, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallForwardingOption(const MozCallForwardingOptions& aOptions,
                                           ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -600,30 +603,30 @@ MobileConnection::SetCallForwardingOptio
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SetCallForwarding(options, requestCallback);
+  nsresult rv = mService->SetCallForwarding(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallBarringOption(const MozCallBarringOptions& aOptions,
                                        ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -635,30 +638,30 @@ MobileConnection::GetCallBarringOption(c
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetCallBarring(options, requestCallback);
+  nsresult rv = mService->GetCallBarring(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallBarringOption(const MozCallBarringOptions& aOptions,
                                        ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -670,30 +673,30 @@ MobileConnection::SetCallBarringOption(c
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SetCallBarring(options, requestCallback);
+  nsresult rv = mService->SetCallBarring(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::ChangeCallBarringPassword(const MozCallBarringOptions& aOptions,
                                             ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -705,147 +708,147 @@ MobileConnection::ChangeCallBarringPassw
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->ChangeCallBarringPassword(options, requestCallback);
+  nsresult rv = mService->ChangeCallBarringPassword(mClientId, options,
+                                                    requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallWaitingOption(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->GetCallWaiting(requestCallback);
+  nsresult rv = mService->GetCallWaiting(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallWaitingOption(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SetCallWaiting(aEnabled, requestCallback);
+  nsresult rv = mService->SetCallWaiting(mClientId, aEnabled, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallingLineIdRestriction(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->GetCallingLineIdRestriction(requestCallback);
+  nsresult rv = mService->GetCallingLineIdRestriction(mClientId,
+                                                      requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallingLineIdRestriction(uint16_t aMode,
                                               ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv =
-    mMobileConnection->SetCallingLineIdRestriction(aMode, requestCallback);
+  nsresult rv = mService->SetCallingLineIdRestriction(mClientId, aMode,
+                                                      requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::ExitEmergencyCbMode(ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->ExitEmergencyCbMode(requestCallback);
+  nsresult rv = mService->ExitEmergencyCbMode(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetRadioEnabled(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mMobileConnection) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsRefPtr<MobileConnectionCallback> requestCallback =
     new MobileConnectionCallback(GetOwner(), request);
 
-  nsresult rv = mMobileConnection->SetRadioEnabled(aEnabled, requestCallback);
+  nsresult rv = mService->SetRadioEnabled(mClientId, aEnabled, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
 
--- a/dom/mobileconnection/MobileConnection.h
+++ b/dom/mobileconnection/MobileConnection.h
@@ -1,19 +1,19 @@
 /* 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_MobileConnection_h
 #define mozilla_dom_MobileConnection_h
 
+#include "MobileConnectionInfo.h"
+#include "MobileNetworkInfo.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMRequest.h"
-#include "mozilla/dom/MobileConnectionInfo.h"
-#include "mozilla/dom/MobileNetworkInfo.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIMobileConnectionService.h"
 #include "nsWeakPtr.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -158,17 +158,18 @@ public:
   IMPL_EVENT_HANDLER(iccchange)
   IMPL_EVENT_HANDLER(radiostatechange)
   IMPL_EVENT_HANDLER(clirmodechange)
 
 private:
   ~MobileConnection();
 
 private:
-  nsCOMPtr<nsIMobileConnection> mMobileConnection;
+  uint32_t mClientId;
+  nsCOMPtr<nsIMobileConnectionService> mService;
   nsRefPtr<Listener> mListener;
   nsRefPtr<MobileConnectionInfo> mVoice;
   nsRefPtr<MobileConnectionInfo> mData;
 
   bool
   CheckPermission(const char* aType) const;
 
   void
--- a/dom/mobileconnection/MobileConnectionArray.cpp
+++ b/dom/mobileconnection/MobileConnectionArray.cpp
@@ -1,51 +1,59 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/MobileConnectionArray.h"
+#include "MobileConnectionArray.h"
 #include "mozilla/dom/MozMobileConnectionArrayBinding.h"
 #include "mozilla/Preferences.h"
-#include "nsServiceManagerUtils.h"
-
-// Service instantiation
-#include "ipc/MobileConnectionIPCService.h"
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-#include "nsIGonkMobileConnectionService.h"
-#endif
-#include "nsXULAppAPI.h" // For XRE_GetProcessType()
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MobileConnectionArray,
                                       mWindow,
                                       mMobileConnections)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MobileConnectionArray)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MobileConnectionArray)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MobileConnectionArray)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 MobileConnectionArray::MobileConnectionArray(nsPIDOMWindow* aWindow)
-  : mLengthInitialized(false)
+  : mInitialized(false)
   , mWindow(aWindow)
 {
+  uint32_t numRil = mozilla::Preferences::GetUint("ril.numRadioInterfaces", 1);
+  MOZ_ASSERT(numRil > 0);
+
+  mMobileConnections.SetLength(numRil);
+
   SetIsDOMBinding();
 }
 
 MobileConnectionArray::~MobileConnectionArray()
 {
 }
 
+void
+MobileConnectionArray::Init()
+{
+  mInitialized = true;
+
+  for (uint32_t id = 0; id < mMobileConnections.Length(); id++) {
+    nsRefPtr<MobileConnection> mobileConnection = new MobileConnection(mWindow, id);
+    mMobileConnections[id] = mobileConnection;
+  }
+}
+
 nsPIDOMWindow*
 MobileConnectionArray::GetParentObject() const
 {
   MOZ_ASSERT(mWindow);
   return mWindow;
 }
 
 JSObject*
@@ -57,58 +65,25 @@ MobileConnectionArray::WrapObject(JSCont
 MobileConnection*
 MobileConnectionArray::Item(uint32_t aIndex)
 {
   bool unused;
   return IndexedGetter(aIndex, unused);
 }
 
 uint32_t
-MobileConnectionArray::Length()
+MobileConnectionArray::Length() const
 {
-  if (!mLengthInitialized) {
-    mLengthInitialized = true;
-
-    nsCOMPtr<nsIMobileConnectionService> service =
-      do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-    NS_ENSURE_TRUE(service, 0);
-
-    uint32_t length = 0;
-    nsresult rv = service->GetNumItems(&length);
-    NS_ENSURE_SUCCESS(rv, 0);
-
-    mMobileConnections.SetLength(length);
-  }
-
   return mMobileConnections.Length();
 }
 
 MobileConnection*
 MobileConnectionArray::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
-
-  aFound = aIndex < Length();
-  if (!aFound) {
-    return nullptr;
-  }
-
-  if (!mMobileConnections[aIndex]) {
-    mMobileConnections[aIndex] = new MobileConnection(mWindow, aIndex);
+  if (!mInitialized) {
+    Init();
   }
 
-  return mMobileConnections[aIndex];
-}
-
-already_AddRefed<nsIMobileConnectionService>
-NS_CreateMobileConnectionService()
-{
-  nsCOMPtr<nsIMobileConnectionService> service;
+  aFound = false;
+  aFound = aIndex < mMobileConnections.Length();
 
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    service = new mozilla::dom::mobileconnection::MobileConnectionIPCService();
-  } else {
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    service = do_CreateInstance(GONK_MOBILECONNECTION_SERVICE_CONTRACTID);
-#endif
-  }
-
-  return service.forget();
+  return aFound ? mMobileConnections[aIndex] : nullptr;
 }
--- a/dom/mobileconnection/MobileConnectionArray.h
+++ b/dom/mobileconnection/MobileConnectionArray.h
@@ -2,18 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_network_MobileConnectionArray_h__
 #define mozilla_dom_network_MobileConnectionArray_h__
 
-#include "mozilla/dom/MobileConnection.h"
 #include "nsWrapperCache.h"
+#include "MobileConnection.h"
 
 namespace mozilla {
 namespace dom {
 
 class MobileConnectionArray MOZ_FINAL : public nsISupports
                                       , public nsWrapperCache
 {
 public:
@@ -29,25 +29,28 @@ public:
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   //  WebIDL
   MobileConnection*
   Item(uint32_t aIndex);
 
   uint32_t
-  Length();
+  Length() const;
 
   MobileConnection*
   IndexedGetter(uint32_t aIndex, bool& aFound);
 
 private:
   ~MobileConnectionArray();
 
-  bool mLengthInitialized;
+  void
+  Init();
+
+  bool mInitialized;
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsTArray<nsRefPtr<MobileConnection>> mMobileConnections;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/mobileconnection/MobileConnectionCallback.cpp
+++ b/dom/mobileconnection/MobileConnectionCallback.cpp
@@ -1,24 +1,22 @@
 /* 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 "MobileConnectionCallback.h"
 
-#include "mozilla/dom/DOMMMIError.h"
+#include "DOMMMIError.h"
 #include "mozilla/dom/MobileNetworkInfo.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 
-namespace mozilla {
-namespace dom {
-namespace mobileconnection {
+using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(MobileConnectionCallback, nsIMobileConnectionCallback)
 
 MobileConnectionCallback::MobileConnectionCallback(nsPIDOMWindow* aWindow,
                                                    DOMRequest* aRequest)
   : mWindow(aWindow)
   , mRequest(aRequest)
 {
@@ -314,12 +312,8 @@ MobileConnectionCallback::NotifyError(co
     case 3:
       error = new DOMMMIError(mWindow, aName, aMessage, aServiceCode,
                               Nullable<int16_t>(int16_t(aInfo)));
       return rs->FireDetailedError(mRequest, error);
   }
 
   return NS_ERROR_FAILURE;
 }
-
-} // namespace mobileconnection
-} // namespace dom
-} // namespace mozilla
--- a/dom/mobileconnection/MobileConnectionCallback.h
+++ b/dom/mobileconnection/MobileConnectionCallback.h
@@ -1,32 +1,31 @@
 /* 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_mobileconnection_MobileConnectionCallback_h
-#define mozilla_dom_mobileconnection_MobileConnectionCallback_h
+#ifndef mozilla_dom_MobileConnectionCallback_h
+#define mozilla_dom_MobileConnectionCallback_h
 
 #include "mozilla/dom/DOMRequest.h"
-#include "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h"
+#include "mozilla/dom/MobileConnectionIPCSerializer.h"
 #include "nsCOMPtr.h"
 #include "nsIMobileConnectionService.h"
 
 namespace mozilla {
 namespace dom {
-namespace mobileconnection {
 
 /**
  * A callback object for handling asynchronous request/response. This object is
  * created when an asynchronous request is made and should be destroyed after
  * Notify*Success/Error is called.
  * The modules hold the reference of MobileConnectionCallback in OOP mode and
  * non-OOP mode are different.
  * - OOP mode: MobileConnectionRequestChild
- * - non-OOP mode: MobileConnectionService
+ * - non-OOP mode: MobileConnectionGonkService
  * The reference should be released after Notify*Success/Error is called.
  */
 class MobileConnectionCallback MOZ_FINAL : public nsIMobileConnectionCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMOBILECONNECTIONCALLBACK
 
@@ -68,13 +67,12 @@ private:
 
   nsresult
   NotifySuccess(JS::Handle<JS::Value> aResult);
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsRefPtr<DOMRequest> mRequest;
 };
 
-} // namespace mobileconnection
-} // namespace dom
-} // namespace mozilla
+} // name space dom
+} // name space mozilla
 
-#endif // mozilla_dom_mobileconnection_MobileConnectionCallback_h
+#endif // mozilla_dom_MobileConnectionCallback_h
--- a/dom/mobileconnection/MobileConnectionInfo.cpp
+++ b/dom/mobileconnection/MobileConnectionInfo.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "mozilla/dom/MobileConnectionInfo.h"
+#include "MobileConnectionInfo.h"
 
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "jsapi.h"
 
 #define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum)      \
 {                                                                       \
   _enum.SetNull();                                                      \
--- a/dom/mobileconnection/MobileConnectionInfo.h
+++ b/dom/mobileconnection/MobileConnectionInfo.h
@@ -2,18 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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_MobileConnectionInfo_h
 #define mozilla_dom_MobileConnectionInfo_h
 
-#include "mozilla/dom/MobileCellInfo.h"
-#include "mozilla/dom/MobileNetworkInfo.h"
+#include "MobileCellInfo.h"
+#include "MobileNetworkInfo.h"
 #include "mozilla/dom/MozMobileConnectionInfoBinding.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/mobileconnection/MobileNetworkInfo.cpp
+++ b/dom/mobileconnection/MobileNetworkInfo.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/MobileNetworkInfo.h"
+#include "MobileNetworkInfo.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MobileNetworkInfo, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MobileNetworkInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MobileNetworkInfo)
 
rename from dom/mobileconnection/gonk/MobileConnectionService.js
rename to dom/mobileconnection/gonk/MobileConnectionGonkService.js
--- a/dom/mobileconnection/gonk/MobileConnectionService.js
+++ b/dom/mobileconnection/gonk/MobileConnectionGonkService.js
@@ -9,23 +9,21 @@ const {classes: Cc, interfaces: Ci, util
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/systemlibs.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
-const GONK_MOBILECONNECTIONSERVICE_CONTRACTID =
-  "@mozilla.org/mobileconnection/gonkmobileconnectionservice;1";
+const MOBILECONNECTIONGONKSERVICE_CONTRACTID =
+  "@mozilla.org/mobileconnection/mobileconnectiongonkservice;1";
 
-const GONK_MOBILECONNECTIONSERVICE_CID =
+const MOBILECONNECTIONGONKSERVICE_CID =
   Components.ID("{05e20430-fe65-4984-8df9-a6a504b24a91}");
-const MOBILECONNECTIONINFO_CID =
-  Components.ID("{8162b3c0-664b-45f6-96cd-f07b4e193b0e}");
 const MOBILENETWORKINFO_CID =
   Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
 const MOBILECELLINFO_CID =
   Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}");
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID  = "nsPref:changed";
 const NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID = "network-active-changed";
@@ -41,17 +39,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
 let DEBUG = RIL.DEBUG_RIL;
 function debug(s) {
-  dump("MobileConnectionService: " + s + "\n");
+  dump("MobileConnectionGonkService: " + s + "\n");
 }
 
 function MobileNetworkInfo() {
   this.shortName = null;
   this.longName = null;
   this.mcc = null;
   this.mnc = null;
   this.stat = null;
@@ -80,37 +78,16 @@ MobileCellInfo.prototype = {
   classID:        MOBILECELLINFO_CID,
   classInfo:      XPCOMUtils.generateCI({
     classID:          MOBILECELLINFO_CID,
     classDescription: "MobileCellInfo",
     interfaces:       [Ci.nsIMobileCellInfo]
   })
 };
 
-function MobileConnectionInfo() {}
-MobileConnectionInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionInfo]),
-  classID: MOBILECONNECTIONINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID: MOBILECONNECTIONINFO_CID,
-    classDescription: "MobileConnectionInfo",
-    interfaces: [Ci.nsIMobileConnectionInfo]
-  }),
-
-  state: null,
-  connected: false,
-  emergencyCallsOnly: false,
-  roaming: false,
-  network: null,
-  cell: null,
-  type: null,
-  signalStrength: null,
-  relSignalStrength: null
-};
-
 function CallForwardingOptions(aOptions) {
   this.active = aOptions.active;
   this.action = aOptions.action;
   this.reason = aOptions.reason;
   this.number = aOptions.number;
   this.timeSeconds = aOptions.timeSeconds;
   this.serviceClass = aOptions.serviceClass;
 }
@@ -137,37 +114,53 @@ MMIResult.prototype = {
 function MobileConnectionProvider(aClientId, aRadioInterface) {
   this._clientId = aClientId;
   this._radioInterface = aRadioInterface;
   this._operatorInfo = {};
   // An array of nsIMobileConnectionListener instances.
   this._listeners = [];
 
   this.supportedNetworkTypes = this._getSupportedNetworkTypes();
-  this.voice = new MobileConnectionInfo();
-  this.data = new MobileConnectionInfo();
+  // These objects implement the nsIMobileConnectionInfo interface,
+  // although the actual implementation lives in the content process. So are
+  // the child attributes `network` and `cell`, which implement
+  // nsIMobileNetworkInfo and nsIMobileCellInfo respectively.
+  this.voiceInfo = {connected: false,
+                    emergencyCallsOnly: false,
+                    roaming: false,
+                    network: null,
+                    cell: null,
+                    type: null,
+                    signalStrength: null,
+                    relSignalStrength: null};
+  this.dataInfo = {connected: false,
+                   emergencyCallsOnly: false,
+                   roaming: false,
+                   network: null,
+                   cell: null,
+                   type: null,
+                   signalStrength: null,
+                   relSignalStrength: null};
 }
 MobileConnectionProvider.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnection]),
-
   _clientId: null,
   _radioInterface: null,
   _operatorInfo: null,
   _listeners: null,
 
   /**
    * The networks that are currently trying to be selected (or "automatic").
    * This helps ensure that only one network per client is selected at a time.
    */
   _selectingNetwork: null,
 
-  voice: null,
-  data: null,
+  voiceInfo: null,
+  dataInfo: null,
   iccId: null,
-  networkSelectionMode: null,
+  networkSelectMode: null,
   radioState: null,
   lastKnownNetwork: null,
   lastKnownHomeNetwork: null,
   supportedNetworkTypes: null,
 
   /**
    * A utility function to dump debug message.
    */
@@ -214,53 +207,53 @@ MobileConnectionProvider.prototype = {
     return supportedNetworkTypes;
   },
 
   /**
    * Helper for guarding us against invalid reason values for call forwarding.
    */
   _isValidCallForwardingReason: function(aReason) {
     switch (aReason) {
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL:
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY:
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_NO_REPLY:
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE:
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CALL_FORWARDING:
-      case Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_UNCONDITIONAL:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_MOBILE_BUSY:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_NO_REPLY:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_NOT_REACHABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_ALL_CALL_FORWARDING:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING:
         return true;
       default:
         return false;
     }
   },
 
   /**
    * Helper for guarding us against invalid action values for call forwarding.
    */
   _isValidCallForwardingAction: function(aAction) {
     switch (aAction) {
-      case Ci.nsIMobileConnection.CALL_FORWARD_ACTION_DISABLE:
-      case Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ENABLE:
-      case Ci.nsIMobileConnection.CALL_FORWARD_ACTION_REGISTRATION:
-      case Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ERASURE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_DISABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_ENABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_REGISTRATION:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_ERASURE:
         return true;
       default:
         return false;
     }
   },
 
   /**
    * Helper for guarding us against invalid program values for call barring.
    */
   _isValidCallBarringProgram: function(aProgram) {
     switch (aProgram) {
-      case Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING:
-      case Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL:
-      case Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME:
-      case Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_INCOMING:
-      case Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_INCOMING_ROAMING:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_ALL_OUTGOING:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_ALL_INCOMING:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_INCOMING_ROAMING:
         return true;
       default:
         return false;
     }
   },
 
   /**
    * Helper for guarding us against invalid options for call barring.
@@ -280,32 +273,32 @@ MobileConnectionProvider.prototype = {
     return true;
   },
 
   /**
    * Helper for guarding us against invalid mode for clir.
    */
   _isValidClirMode: function(aMode) {
     switch (aMode) {
-      case Ci.nsIMobileConnection.CLIR_DEFAULT:
-      case Ci.nsIMobileConnection.CLIR_INVOCATION:
-      case Ci.nsIMobileConnection.CLIR_SUPPRESSION:
+      case Ci.nsIMobileConnectionService.CLIR_DEFAULT:
+      case Ci.nsIMobileConnectionService.CLIR_INVOCATION:
+      case Ci.nsIMobileConnectionService.CLIR_SUPPRESSION:
         return true;
       default:
         return false;
     }
   },
 
   /**
    * Fix the roaming. RIL can report roaming in some case it is not
    * really the case. See bug 787967
    */
   _checkRoamingBetweenOperators: function(aNetworkInfo) {
     // TODO: Bug 864489 - B2G RIL: use ipdl as IPC in MozIccManager
-    // Should get iccInfo from GonkIccProvider.
+    // Should get iccInfo from IccGonkProvider.
     let iccInfo = this._radioInterface.rilContext.iccInfo;
     let operator = aNetworkInfo.network;
     let state = aNetworkInfo.state;
 
     if (!iccInfo || !operator ||
         state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
       return false;
     }
@@ -388,31 +381,31 @@ MobileConnectionProvider.prototype = {
         if (DEBUG) {
           this._debug("listener for " + aName + " threw an exception: " + e);
         }
       }
     }
   },
 
   updateVoiceInfo: function(aNewInfo, aBatch = false) {
-    let isUpdated = this._updateInfo(this.voice, aNewInfo);
+    let isUpdated = this._updateInfo(this.voiceInfo, aNewInfo);
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (this.voice.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
-      this.voice.cell = null;
-      this.voice.network = null;
-      this.voice.signalStrength = null;
-      this.voice.relSignalStrength = null;
+    if (this.voiceInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      this.voiceInfo.cell = null;
+      this.voiceInfo.network = null;
+      this.voiceInfo.signalStrength = null;
+      this.voiceInfo.relSignalStrength = null;
     } else {
-      this.voice.network = this._operatorInfo;
+      this.voiceInfo.network = this._operatorInfo;
     }
 
     // Check roaming state
-    isUpdated = this._checkRoamingBetweenOperators(this.voice) || isUpdated;
+    isUpdated = this._checkRoamingBetweenOperators(this.voiceInfo) || isUpdated;
 
     if (isUpdated && !aBatch) {
       this.deliverListenerEvent("notifyVoiceChanged");
     }
   },
 
   updateDataInfo: function(aNewInfo, aBatch = false) {
     let isUpdated = false;
@@ -422,31 +415,31 @@ MobileConnectionProvider.prototype = {
     let active = gNetworkManager.active;
     aNewInfo.connected = false;
     if (active &&
         active.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
         active.serviceId === this._clientId) {
       aNewInfo.connected = true;
     }
 
-    isUpdated = this._updateInfo(this.data, aNewInfo);
+    isUpdated = this._updateInfo(this.dataInfo, aNewInfo);
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (this.data.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
-      this.data.cell = null;
-      this.data.network = null;
-      this.data.signalStrength = null;
-      this.data.relSignalStrength = null;
+    if (this.dataInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      this.dataInfo.cell = null;
+      this.dataInfo.network = null;
+      this.dataInfo.signalStrength = null;
+      this.dataInfo.relSignalStrength = null;
     } else {
-      this.data.network = this._operatorInfo;
+      this.dataInfo.network = this._operatorInfo;
     }
 
     // Check roaming state
-    isUpdated = this._checkRoamingBetweenOperators(this.data) || isUpdated;
+    isUpdated = this._checkRoamingBetweenOperators(this.dataInfo) || isUpdated;
 
     if (isUpdated && !aBatch) {
       this.deliverListenerEvent("notifyDataChanged");
     }
   },
 
   updateOperatorInfo: function(aNewInfo, aBatch = false) {
     let isUpdated = this._updateInfo(this._operatorInfo, aNewInfo);
@@ -460,39 +453,39 @@ MobileConnectionProvider.prototype = {
         }
 
         this.lastKnownNetwork = network;
         this.deliverListenerEvent("notifyLastKnownNetworkChanged");
       }
     }
 
     // If the voice is unregistered, no need to send notification.
-    if (this.voice.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
+    if (this.voiceInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
         isUpdated && !aBatch) {
       this.deliverListenerEvent("notifyVoiceChanged");
     }
 
     // If the data is unregistered, no need to send notification.
-    if (this.data.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
+    if (this.dataInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
         isUpdated && !aBatch) {
       this.deliverListenerEvent("notifyDataChanged");
     }
   },
 
   updateSignalInfo: function(aNewInfo, aBatch = false) {
     // If the voice is not registered, no need to update signal information.
-    if (this.voice.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
-      if (this._updateInfo(this.voice, aNewInfo.voice) && !aBatch) {
+    if (this.voiceInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      if (this._updateInfo(this.voiceInfo, aNewInfo.voice) && !aBatch) {
         this.deliverListenerEvent("notifyVoiceChanged");
       }
     }
 
     // If the data is not registered, no need to update signal information.
-    if (this.data.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
-      if (this._updateInfo(this.data, aNewInfo.data) && !aBatch) {
+    if (this.dataInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      if (this._updateInfo(this.dataInfo, aNewInfo.data) && !aBatch) {
         this.deliverListenerEvent("notifyDataChanged");
       }
     }
   },
 
   updateIccId: function(aIccId) {
     if (this.iccId === aIccId) {
       return;
@@ -506,21 +499,16 @@ MobileConnectionProvider.prototype = {
     if (this.radioState === aRadioState) {
       return;
     }
 
     this.radioState = aRadioState;
     this.deliverListenerEvent("notifyRadioStateChanged");
   },
 
-  getSupportedNetworkTypes: function(aTypes) {
-    aTypes.value = this.supportedNetworkTypes.slice();
-    return aTypes.value.length;
-  },
-
   getNetworks: function(aCallback) {
     this._radioInterface.sendWorkerMessage("getAvailableNetworks", null,
                                            (function(aResponse) {
       if (aResponse.errorMsg) {
         aCallback.notifyError(aResponse.errorMsg);
         return false;
       }
 
@@ -957,41 +945,41 @@ MobileConnectionProvider.prototype = {
       }
 
       aCallback.notifySuccess();
       return true;
     }).bind(this));
   },
 };
 
-function MobileConnectionService() {
+function MobileConnectionGonkService() {
   this._providers = [];
 
   let numClients = gRadioInterfaceLayer.numRadioInterfaces;
   for (let i = 0; i < numClients; i++) {
     let radioInterface = gRadioInterfaceLayer.getRadioInterface(i);
     let provider = new MobileConnectionProvider(i, radioInterface);
     this._providers.push(provider);
   }
 
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
   Services.obs.addObserver(this, NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID, false);
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   debug("init complete");
 }
-MobileConnectionService.prototype = {
-  classID: GONK_MOBILECONNECTIONSERVICE_CID,
-  classInfo: XPCOMUtils.generateCI({classID: GONK_MOBILECONNECTIONSERVICE_CID,
-                                    contractID: GONK_MOBILECONNECTIONSERVICE_CONTRACTID,
-                                    classDescription: "MobileConnectionService",
-                                    interfaces: [Ci.nsIGonkMobileConnectionService,
+MobileConnectionGonkService.prototype = {
+  classID: MOBILECONNECTIONGONKSERVICE_CID,
+  classInfo: XPCOMUtils.generateCI({classID: MOBILECONNECTIONGONKSERVICE_CID,
+                                    contractID: MOBILECONNECTIONGONKSERVICE_CONTRACTID,
+                                    classDescription: "MobileConnectionGonkService",
+                                    interfaces: [Ci.nsIMobileConnectionGonkService,
                                                  Ci.nsIMobileConnectionService],
                                     flags: Ci.nsIClassInfo.SINGLETON}),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGonkMobileConnectionService,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionGonkService,
                                          Ci.nsIMobileConnectionService,
                                          Ci.nsIObserver]),
 
   // An array of MobileConnectionProvider instances.
   _providers: null,
 
   _shutdown: function() {
     Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
@@ -1004,120 +992,435 @@ MobileConnectionService.prototype = {
       DEBUG = RIL.DEBUG_RIL ||
               Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
     } catch (e) {}
   },
 
   /**
    * nsIMobileConnectionService interface.
    */
-  get numItems() {
-    return this._providers.length;
+  registerListener: function(aClientId, aListener) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.registerListener(aListener);
+  },
+
+  unregisterListener: function(aClientId, aListener) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.unregisterListener(aListener);
+  },
+
+  getVoiceConnectionInfo: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.voiceInfo;
+  },
+
+  getDataConnectionInfo: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.dataInfo;
+  },
+
+  getIccId: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.iccId;
+  },
+
+  getNetworkSelectionMode: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.networkSelectMode;
+  },
+
+  getRadioState: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.radioState;
   },
 
-  getItemByServiceId: function(aServiceId) {
-    let provider = this._providers[aServiceId];
+  getLastKnownNetwork: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.lastKnownNetwork;
+  },
+
+  getLastKnownHomeNetwork: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.lastKnownHomeNetwork;
+  },
+
+  getSupportedNetworkTypes: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.supportedNetworkTypes;
+  },
+
+  getNetworks: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getNetworks(aCallback);
+  },
+
+  selectNetwork: function(aClientId, aNetwork, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.selectNetwork(aNetwork, aCallback);
+  },
+
+  selectNetworkAutomatically: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.selectNetworkAutomatically(aCallback);
+  },
+
+  setPreferredNetworkType: function(aClientId, aType, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setPreferredNetworkType(aType, aCallback);
+  },
+
+  getPreferredNetworkType: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getPreferredNetworkType(aCallback);
+  },
+
+  setRoamingPreference: function(aClientId, aMode, aCallback) {
+    let provider = this._providers[aClientId];
     if (!provider) {
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
-    return provider;
+    provider.setRoamingPreference(aMode, aCallback);
+  },
+
+  getRoamingPreference: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getRoamingPreference(aCallback);
+  },
+
+  setVoicePrivacyMode: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setVoicePrivacyMode(aEnabled, aCallback);
+  },
+
+  getVoicePrivacyMode: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getVoicePrivacyMode(aCallback);
+  },
+
+  sendMMI: function(aClientId, aMmi, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.sendMMI(aMmi, aCallback);
+  },
+
+  cancelMMI: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.cancelMMI(aCallback);
+  },
+
+  setCallForwarding: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallForwarding(aOptions, aCallback);
+  },
+
+  getCallForwarding: function(aClientId, aReason, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallForwarding(aReason, aCallback);
+  },
+
+  setCallBarring: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallBarring(aOptions, aCallback);
+  },
+
+  getCallBarring: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallBarring(aOptions, aCallback);
+  },
+
+  changeCallBarringPassword: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.changeCallBarringPassword(aOptions, aCallback);
+  },
+
+  setCallWaiting: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallWaiting(aEnabled, aCallback);
+  },
+
+  getCallWaiting: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallWaiting(aCallback);
+  },
+
+  setCallingLineIdRestriction: function(aClientId, aMode, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallingLineIdRestriction(aMode, aCallback);
+  },
+
+  getCallingLineIdRestriction: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallingLineIdRestriction(aCallback);
+  },
+
+  exitEmergencyCbMode: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.exitEmergencyCbMode(aCallback);
+  },
+
+  setRadioEnabled: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setRadioEnabled(aEnabled, aCallback);
   },
 
   /**
-   * nsIGonkMobileConnectionService interface.
+   * nsIMobileConnectionGonkService interface.
    */
   notifyVoiceInfoChanged: function(aClientId, aVoiceInfo) {
     if (DEBUG) {
       debug("notifyVoiceInfoChanged for " + aClientId + ": " +
             JSON.stringify(aVoiceInfo));
     }
 
-    this.getItemByServiceId(aClientId).updateVoiceInfo(aVoiceInfo);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateVoiceInfo(aVoiceInfo);
   },
 
   notifyDataInfoChanged: function(aClientId, aDataInfo) {
     if (DEBUG) {
       debug("notifyDataInfoChanged for " + aClientId + ": " +
             JSON.stringify(aDataInfo));
     }
 
-    this.getItemByServiceId(aClientId).updateDataInfo(aDataInfo);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateDataInfo(aDataInfo);
   },
 
   notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
     if (DEBUG) {
       debug("notifyUssdReceived for " + aClientId + ": " +
             aMessage + " (sessionEnded : " + aSessionEnded + ")");
     }
 
-    this.getItemByServiceId(aClientId)
-        .deliverListenerEvent("notifyUssdReceived", [aMessage, aSessionEnded]);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyUssdReceived",
+                                [aMessage, aSessionEnded]);
 
     let info = {
       message: aMessage,
       sessionEnded: aSessionEnded,
       serviceId: aClientId
     };
 
     gSystemMessenger.broadcastMessage("ussd-received", info);
   },
 
   notifyDataError: function(aClientId, aMessage) {
     if (DEBUG) {
       debug("notifyDataError for " + aClientId + ": " + aMessage);
     }
 
-    this.getItemByServiceId(aClientId)
-        .deliverListenerEvent("notifyDataError", [aMessage]);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyDataError", [aMessage]);
   },
 
   notifyEmergencyCallbackModeChanged: function(aClientId, aActive, aTimeoutMs) {
     if (DEBUG) {
       debug("notifyEmergencyCbModeChanged for " + aClientId + ": " +
             JSON.stringify({active: aActive, timeoutMs: aTimeoutMs}));
     }
 
-    this.getItemByServiceId(aClientId)
-        .deliverListenerEvent("notifyEmergencyCbModeChanged",
-                              [aActive, aTimeoutMs]);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyEmergencyCbModeChanged",
+                                [aActive, aTimeoutMs]);
   },
 
   notifyOtaStatusChanged: function(aClientId, aStatus) {
     if (DEBUG) {
       debug("notifyOtaStatusChanged for " + aClientId + ": " + aStatus);
     }
 
-    this.getItemByServiceId(aClientId)
-        .deliverListenerEvent("notifyOtaStatusChanged", [aStatus]);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyOtaStatusChanged", [aStatus]);
   },
 
   notifyIccChanged: function(aClientId, aIccId) {
     if (DEBUG) {
       debug("notifyIccChanged for " + aClientId + ": " + aIccId);
     }
 
-    this.getItemByServiceId(aClientId).updateIccId(aIccId);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateIccId(aIccId);
   },
 
   notifyRadioStateChanged: function(aClientId, aRadioState) {
     if (DEBUG) {
       debug("notifyRadioStateChanged for " + aClientId + ": " + aRadioState);
     }
 
-    this.getItemByServiceId(aClientId).updateRadioState(aRadioState);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateRadioState(aRadioState);
   },
 
   notifyNetworkInfoChanged: function(aClientId, aNetworkInfo) {
     if (DEBUG) {
       debug("notifyNetworkInfoChanged for " + aClientId + ": " +
             JSON.stringify(aNetworkInfo));
     }
 
-    let provider = this.getItemByServiceId(aClientId);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
 
     let isVoiceUpdated = false;
     let isDataUpdated = false;
     let operatorMessage = aNetworkInfo[RIL.NETWORK_INFO_OPERATOR];
     let voiceMessage = aNetworkInfo[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
     let dataMessage = aNetworkInfo[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
     let signalMessage = aNetworkInfo[RIL.NETWORK_INFO_SIGNAL];
     let selectionMessage = aNetworkInfo[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
@@ -1153,77 +1456,98 @@ MobileConnectionService.prototype = {
   },
 
   notifySignalStrengthChanged: function(aClientId, aSignal) {
     if (DEBUG) {
       debug("notifySignalStrengthChanged for " + aClientId + ": " +
             JSON.stringify(aSignal));
     }
 
-    this.getItemByServiceId(aClientId).updateSignalInfo(aSignal);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateSignalInfo(aSignal);
   },
 
   notifyOperatorChanged: function(aClientId, aOperator) {
     if (DEBUG) {
       debug("notifyOperatorChanged for " + aClientId + ": " +
             JSON.stringify(aOperator));
     }
 
-    this.getItemByServiceId(aClientId).updateOperatorInfo(aOperator);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateOperatorInfo(aOperator);
   },
 
   notifyNetworkSelectModeChanged: function(aClientId, aMode) {
     if (DEBUG) {
       debug("notifyNetworkSelectModeChanged for " + aClientId + ": " + aMode);
     }
 
-    let provider = this.getItemByServiceId(aClientId);
-    if (provider.networkSelectionMode === aMode) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    if (provider.networkSelectMode === aMode) {
       return;
     }
 
-    provider.networkSelectionMode = aMode;
+    provider.networkSelectMode = aMode;
     provider.deliverListenerEvent("notifyNetworkSelectionModeChanged");
   },
 
   notifySpnAvailable: function(aClientId) {
     if (DEBUG) {
       debug("notifySpnAvailable for " + aClientId);
     }
 
-    let provider = this.getItemByServiceId(aClientId);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
 
     // Update voice roaming state
     provider.updateVoiceInfo({});
 
     // Update data roaming state
     provider.updateDataInfo({});
   },
 
   notifyLastHomeNetworkChanged: function(aClientId, aNetwork) {
     if (DEBUG) {
       debug("notifyLastHomeNetworkChanged for " + aClientId + ": " + aNetwork);
     }
 
-    let provider = this.getItemByServiceId(aClientId);
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
     if (provider.lastKnownHomeNetwork === aNetwork) {
       return;
     }
 
     provider.lastKnownHomeNetwork = aNetwork;
     provider.deliverListenerEvent("notifyLastKnownHomeNetworkChanged");
   },
 
   /**
    * nsIObserver interface.
    */
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID:
-        for (let i = 0; i < this.numItems; i++) {
+        for (let i = 0; i < this._providers.length; i++) {
           let provider = this._providers[i];
           // Update connected flag only.
           provider.updateDataInfo({});
         }
         break;
       case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
         if (aData === kPrefRilDebuggingEnabled) {
           this._updateDebugFlag();
@@ -1231,9 +1555,9 @@ MobileConnectionService.prototype = {
         break;
       case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
         this._shutdown();
         break;
     }
   }
 };
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MobileConnectionService]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MobileConnectionGonkService]);
rename from dom/mobileconnection/gonk/MobileConnectionService.manifest
rename to dom/mobileconnection/gonk/MobileConnectionGonkService.manifest
--- a/dom/mobileconnection/gonk/MobileConnectionService.manifest
+++ b/dom/mobileconnection/gonk/MobileConnectionGonkService.manifest
@@ -1,2 +1,2 @@
-component {05e20430-fe65-4984-8df9-a6a504b24a91} MobileConnectionService.js
-contract @mozilla.org/mobileconnection/gonkmobileconnectionservice;1 {05e20430-fe65-4984-8df9-a6a504b24a91}
+component {05e20430-fe65-4984-8df9-a6a504b24a91} MobileConnectionGonkService.js
+contract @mozilla.org/mobileconnection/mobileconnectiongonkservice;1 {05e20430-fe65-4984-8df9-a6a504b24a91}
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/interfaces/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+    'nsICellInfo.idl',
+    'nsIMobileCellInfo.idl',
+    'nsIMobileConnectionInfo.idl',
+    'nsIMobileConnectionService.idl',
+    'nsIMobileNetworkInfo.idl',
+    'nsINeighboringCellInfo.idl',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
+    XPIDL_SOURCES += [
+        'nsIMobileConnectionGonkService.idl',
+    ]
+
+XPIDL_MODULE = 'dom_mobileconnection'
rename from dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl
rename to dom/mobileconnection/interfaces/nsIMobileConnectionGonkService.idl
--- a/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl
+++ b/dom/mobileconnection/interfaces/nsIMobileConnectionGonkService.idl
@@ -1,21 +1,21 @@
 /* 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 "nsIMobileConnectionService.idl"
 
 %{C++
-#define GONK_MOBILECONNECTION_SERVICE_CONTRACTID \
-        "@mozilla.org/mobileconnection/gonkmobileconnectionservice;1"
+#define NS_MOBILECONNECTION_GONK_SERVICE_CONTRACTID \
+        "@mozilla.org/mobileconnection/mobileconnectiongonkservice;1"
 %}
 
-[scriptable, uuid(e54fa0a4-d357-48ef-9a1e-ffc9705b44b1)]
-interface nsIGonkMobileConnectionService : nsIMobileConnectionService
+[scriptable, uuid(c5baceda-247a-4018-855d-ad5b00f2e4e2)]
+interface nsIMobileConnectionGonkService : nsIMobileConnectionService
 {
   void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo);
 
   void notifyVoiceInfoChanged(in unsigned long clientId, in jsval voiceInfo);
 
   void notifyDataInfoChanged(in unsigned long clientId, in jsval dataInfo);
 
   void notifyDataError(in unsigned long clientId, in DOMString message);
--- a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
+++ b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
@@ -1,15 +1,14 @@
 /* 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 nsIMobileConnection;
 interface nsIMobileConnectionInfo;
 interface nsIMobileNetworkInfo;
 interface nsIVariant;
 
 [scriptable, uuid(bc0d4d76-fd3a-4593-818f-cb6ff87fbb55)]
 interface nsIMobileConnectionListener : nsISupports
 {
   /**
@@ -196,34 +195,23 @@ interface nsIMobileConnectionCallback : 
 %{C++
 #define NS_MOBILE_CONNECTION_SERVICE_CID \
   { 0xc6f229d4, 0x16e2, 0x4600, \
     { 0x87, 0x2a, 0x3d, 0x3d, 0xc5, 0xb8, 0x55, 0x41 } }
 #define NS_MOBILE_CONNECTION_SERVICE_CONTRACTID \
   "@mozilla.org/mobileconnection/mobileconnectionservice;1"
 %}
 
-[scriptable, uuid(eaba3c4a-0dd5-4919-b1a2-7812e49dbbcb)]
+/**
+ * XPCOM component (in the content process) that provides the mobile
+ * network information.
+ */
+[scriptable, uuid(b50ad32d-f70e-4729-a947-e8cfdb6ba81f)]
 interface nsIMobileConnectionService : nsISupports
 {
-  readonly attribute unsigned long numItems;
-
-  nsIMobileConnection getItemByServiceId(in unsigned long serviceId);
-};
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsIMobileConnectionService>
-NS_CreateMobileConnectionService();
-%}
-
-[scriptable, uuid(04db7331-54fe-4cf7-9347-b9481350f4c2)]
-interface nsIMobileConnection : nsISupports
-{
   const long ICC_SERVICE_CLASS_VOICE      = (1 << 0);
   const long ICC_SERVICE_CLASS_DATA       = (1 << 1);
   const long ICC_SERVICE_CLASS_FAX        = (1 << 2);
   const long ICC_SERVICE_CLASS_SMS        = (1 << 3);
   const long ICC_SERVICE_CLASS_DATA_SYNC  = (1 << 4);
   const long ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5);
   const long ICC_SERVICE_CLASS_PACKET     = (1 << 6);
   const long ICC_SERVICE_CLASS_PAD        = (1 << 7);
@@ -265,447 +253,540 @@ interface nsIMobileConnection : nsISuppo
    * Calling line identification restriction constants.
    *
    * @see 3GPP TS 27.007 7.7 Defined values.
    */
   const long CLIR_DEFAULT     = 0;
   const long CLIR_INVOCATION  = 1;
   const long CLIR_SUPPRESSION = 2;
 
-  readonly attribute unsigned long serviceId;
-
   /**
-   * Called when any one who is interested in receiving unsolicited messages
-   * from this nsIMobileConnection instance.
+   * Called when a content process registers receiving unsolicited messages from
+   * RadioInterfaceLayer in the chrome process. Only a content process that has
+   * the 'mobileconnection' permission is allowed to register.
    */
-  void registerListener(in nsIMobileConnectionListener listener);
-  void unregisterListener(in nsIMobileConnectionListener listener);
+  void registerListener(in unsigned long clientId,
+                        in nsIMobileConnectionListener listener);
+  void unregisterListener(in unsigned long clientId,
+                          in nsIMobileConnectionListener listener);
 
   /**
-   * String of format '<mcc>-<mnc>'. When changed, listener method
-   * 'notifyLastKnownNetworkChanged' is called.
+   * These two fields require the 'mobilenetwork' permission.
    */
-  readonly attribute DOMString lastKnownNetwork;
+  DOMString getLastKnownNetwork(in unsigned long clientId);
+  DOMString getLastKnownHomeNetwork(in unsigned long clientId);
 
   /**
-   * String of format '<mcc>-<mnc>[-<spn>]'. When changed, listener method
-   * 'notifyLastKnownHomeNetworkChanged' is called.
+   * All fields below require the 'mobileconnection' permission.
    */
-  readonly attribute DOMString lastKnownHomeNetwork;
+
+  /**
+   * Get the connection information about the voice.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
+   *
+   * @return a nsIMobileConnectionInfo
+   */
+  nsIMobileConnectionInfo getVoiceConnectionInfo(in unsigned long clientId);
 
   /**
-   * Connection information about the voice.
+   * Get the connection information about the data.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
+   *
+   * @return a nsIMobileConnectionInfo
    */
-  readonly attribute nsIMobileConnectionInfo voice;
+  nsIMobileConnectionInfo getDataConnectionInfo(in unsigned long clientId);
 
   /**
-   * Connection information about the data.
+   * Get the integrated circuit card identifier of the SIM.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
+   *
+   * @return a DOMString indicates the iccId
    */
-  readonly attribute nsIMobileConnectionInfo data;
+  DOMString getIccId(in unsigned long clientId);
 
   /**
-   * The integrated circuit card identifier of the SIM.
+   * Get the selection mode of the voice and data networks.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
+   *
+   * @return a DOMString
+   *         Possible values: 'automatic', 'manual', null (unknown).
    */
-  readonly attribute DOMString iccId;
+  DOMString getNetworkSelectionMode(in unsigned long clientId);
 
   /**
-   * The selection mode of the voice and data networks. Possible values are
-   * 'automatic', 'manual', null (unknown).
+   * Get the current radio state.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
+   *
+   * @return a DOMString
+   *         Possible values: 'enabling', 'enabled', 'disabling', 'disabled',
+   *         null (unknown).
    */
-  readonly attribute DOMString networkSelectionMode;
+  DOMString getRadioState(in unsigned long clientId);
 
   /**
-   * Current radio state. Possible values are 'enabling', 'enabled',
-   * 'disabling', 'disabled', null (unknown).
-   */
-  readonly attribute DOMString radioState;
-
-  /**
-   * The network types supported by this radio.
+   * Get the network types that are supported by this radio.
+   *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    *
    * @return an array of DOMString
    *         Possible values: 'gsm', 'wcdma', 'cdma', 'evdo', 'lte'.
    */
-  void getSupportedNetworkTypes([array, size_is(length)] out wstring types,
-                                [retval] out unsigned long length);
+  nsIVariant getSupportedNetworkTypes(in unsigned long clientId);
 
   /**
    * Search for available networks.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifyGetNetworksSuccess() will be called. And the
    * result will be an array of nsIMobileNetworkInfo.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getNetworks(in nsIMobileConnectionCallback requestCallback);
+  void getNetworks(in unsigned long clientId,
+                   in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Manually selects the passed in network, overriding the radio's current
    * selection.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param network
    *        The manually selecting network.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void selectNetwork(in nsIMobileNetworkInfo network,
+  void selectNetwork(in unsigned long clientId,
+                     in nsIMobileNetworkInfo network,
                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Tell the radio to automatically select a network.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void selectNetworkAutomatically(in nsIMobileConnectionCallback requestCallback);
+  void selectNetworkAutomatically(in unsigned long clientId,
+                                  in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set preferred network type.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param type
    *        DOMString indicates the desired preferred network type.
    *        Possible values: 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto',
    *        'cdma/evdo', 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo',
    *        'lte/cdma/evdo', 'lte/wcdma/gsm', 'lte/wcdma/gsm/cdma/evdo' or
    *        'lte'.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void setPreferredNetworkType(in DOMString type,
+  void setPreferredNetworkType(in unsigned long clientId,
+                               in DOMString type,
                                in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current preferred network type.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccessString() will be called. And the result
    * will be a string indicating the current preferred network type. The value
    * will be either 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto', 'cdma/evdo',
    * 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo', 'lte/cdma/evdo', 'lte/wcdma/gsm',
    * 'lte/wcdma/gsm/cdma/evdo' or 'lte'.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getPreferredNetworkType(in nsIMobileConnectionCallback requestCallback);
+  void getPreferredNetworkType(in unsigned long clientId,
+                               in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set roaming preference.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param mode
    *        DOMString indicates the desired roaming preference.
    *        Possible values: 'home', 'affiliated', or 'any'.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void setRoamingPreference(in DOMString mode,
+  void setRoamingPreference(in unsigned long clientId,
+                            in DOMString mode,
                             in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current roaming preference.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccessWithString() will be called. And the result
    * will be a string indicating the current roaming preference. The value will
    * be either 'home', 'affiliated', or 'any'.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getRoamingPreference(in nsIMobileConnectionCallback requestCallback);
+  void getRoamingPreference(in unsigned long clientId,
+                            in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set voice privacy preference.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param enabled
    *        Boolean indicates the preferred voice privacy mode used in voice
    *        scrambling in CDMA networks. 'True' means the enhanced voice security
    *        is required.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void setVoicePrivacyMode(in bool enabled,
+  void setVoicePrivacyMode(in unsigned long clientId,
+                           in bool enabled,
                            in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current voice privacy mode.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccessWithBoolean() will be called. And the result
    * will be a boolean indicating the current voice privacy mode.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getVoicePrivacyMode(in nsIMobileConnectionCallback requestCallback);
+  void getVoicePrivacyMode(in unsigned long clientId,
+                           in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Send a MMI message.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param mmi
    *        DOMString containing an MMI string that can be associated to a
    *        USSD request or other RIL functionality.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySendCancelMmiSuccess() will be called. And the
    * result will contain the information about the mmi operation.
    *
    * Otherwise, the notifyError() will be called.
    */
-  void sendMMI(in DOMString mmi,
+  void sendMMI(in unsigned long clientId,
+               in DOMString mmi,
                in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Cancel the current MMI request if one exists.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySendCancelMmiSuccess() will be called. And the
    * result will contain the information about the mmi operation.
    *
    * Otherwise, the notifyError() will be called.
    */
-  void cancelMMI(in nsIMobileConnectionCallback requestCallback);
+  void cancelMMI(in unsigned long clientId,
+                 in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call forwarding options.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param reason
    *        Indicates the reason the call is being forwarded. It shall be one of
    *        the nsIMobileConnectionService.CALL_FORWARD_REASON_* values.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifyGetCallForwardingSuccess() will be called. And the
    * result will be an array of MozCallForwardingOptions.
    * @see MozCallForwardingOptions for the detail of result.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void getCallForwarding(in unsigned short reason,
+  void getCallForwarding(in unsigned long clientId,
+                         in unsigned short reason,
                          in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call forwarding options.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param options
    *        An object containing the call forward rule to set.
    * @see MozCallForwardingOptions for the detail of options.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void setCallForwarding(in jsval options,
+  void setCallForwarding(in unsigned long clientId,
+                         in jsval options,
                          in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call barring status.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param options
    *        An object containing the call barring rule to query. No need to
    *        specify 'enabled' property.
    * @see MozCallBarringOptions for the detail of options.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifyGetCallBarringSuccess() will be called. And the
    * result will contain correct 'enabled' property indicating the status of
    * this rule.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void getCallBarring(in jsval options,
+  void getCallBarring(in unsigned long clientId,
+                      in jsval options,
                       in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call barring option.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param options
    *        An object containing the call barring rule to set.
    * @see MozCallBarringOptions for the detail of options.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void setCallBarring(in jsval options,
+  void setCallBarring(in unsigned long clientId,
+                      in jsval options,
                       in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Change call barring facility password.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param options
    *        An object containing information about pin and newPin, and,
    *        this object must have both "pin" and "newPin" attributes
    *        to change the call barring facility password.
    * @see MozCallBarringOptions for the detail of options.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void changeCallBarringPassword(in jsval options,
+  void changeCallBarringPassword(in unsigned long clientId,
+                                 in jsval options,
                                  in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call waiting options.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param enabled
    *        Boolean indicates the desired call waiting status.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void setCallWaiting(in bool enabled,
+  void setCallWaiting(in unsigned long clientId,
+                      in bool enabled,
                       in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call waiting options.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccessWithBoolean() will be called. And the result
    * will be a boolean indicating the call waiting status.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getCallWaiting(in nsIMobileConnectionCallback requestCallback);
+  void getCallWaiting(in unsigned long clientId,
+                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Enables or disables the presentation of the calling line identity (CLI) to
    * the called party when originating a call.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param clirMode
    *        One of the nsIMobileConnectionService.CLIR_* values.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
    * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  void setCallingLineIdRestriction(in unsigned short clirMode,
+  void setCallingLineIdRestriction(in unsigned long clientId,
+                                   in unsigned short clirMode,
                                    in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current CLIR status.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifyGetClirStatusSuccess() will be called. And the
    * result will be a an object containing CLIR 'n' and 'm' parameter.
    * @see MozClirStatus for the detail of result.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void getCallingLineIdRestriction(in nsIMobileConnectionCallback requestCallback);
+  void getCallingLineIdRestriction(in unsigned long clientId,
+                                   in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Exit emergency callback mode.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
    * 'GenericFailure'.
    */
-  void exitEmergencyCbMode(in nsIMobileConnectionCallback requestCallback);
+  void exitEmergencyCbMode(in unsigned long clientId,
+                           in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set radio enabled/disabled.
    *
+   * @param clientId
+   *        Indicate the RIL client, 0 ~ (number of client - 1).
    * @param enabled
    *        Boolean indicates the desired radio power. True to enable the radio.
    * @param requestCallback
    *        Called when request is finished.
    *
    * If successful, the notifySuccess() will be called.
    *
    * Otherwise, the notifyError() will be called, and the error will be either
    * 'InvalidStateError', 'RadioNotAvailable', 'IllegalSIMorME', or
    * 'GenericFailure'.
    *
    * Note: Request is not available when radioState is null, 'enabling', or
    * 'disabling'. Calling the function in above conditions will receive
    * 'InvalidStateError' error.
    */
-  void setRadioEnabled(in bool enabled,
+  void setRadioEnabled(in unsigned long clientId,
+                       in bool enabled,
                        in nsIMobileConnectionCallback requestCallback);
 };
--- a/dom/mobileconnection/ipc/MobileConnectionChild.cpp
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.cpp
@@ -1,366 +1,157 @@
 /* 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 "mozilla/dom/mobileconnection/MobileConnectionChild.h"
+#include "MobileConnectionChild.h"
 
 #include "MobileConnectionCallback.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "nsComponentManagerUtils.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobileconnection;
 
-NS_IMPL_ISUPPORTS(MobileConnectionChild, nsIMobileConnection)
-
-MobileConnectionChild::MobileConnectionChild(uint32_t aServiceId)
-  : mServiceId(aServiceId)
-  , mLive(true)
-{
-  MOZ_COUNT_CTOR(MobileConnectionChild);
-}
-
 void
 MobileConnectionChild::Init()
 {
   nsIMobileConnectionInfo* rawVoice;
   nsIMobileConnectionInfo* rawData;
+  nsTArray<nsString> types;
 
   SendInit(&rawVoice, &rawData, &mLastNetwork, &mLastHomeNetwork, &mIccId,
-           &mNetworkSelectionMode, &mRadioState, &mSupportedNetworkTypes);
+           &mNetworkSelectionMode, &mRadioState, &types);
 
   // Use dont_AddRef here because this instances is already AddRef-ed in
   // MobileConnectionIPCSerializer.h
   nsCOMPtr<nsIMobileConnectionInfo> voice = dont_AddRef(rawVoice);
   mVoice = new MobileConnectionInfo(nullptr);
   mVoice->Update(voice);
 
   // Use dont_AddRef here because this instances is already AddRef-ed in
   // MobileConnectionIPCSerializer.h
   nsCOMPtr<nsIMobileConnectionInfo> data = dont_AddRef(rawData);
   mData = new MobileConnectionInfo(nullptr);
   mData->Update(data);
+
+
+  // Initial SupportedNetworkTypes
+  nsresult rv;
+  mSupportedNetworkTypes = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  uint32_t arrayLen = types.Length();
+  if (arrayLen == 0) {
+    mSupportedNetworkTypes->SetAsEmptyArray();
+  } else {
+    // Note: The resulting nsIVariant dupes both the array and its elements.
+    const char16_t** array = reinterpret_cast<const char16_t**>
+                               (NS_Alloc(arrayLen * sizeof(const char16_t***)));
+    if (array) {
+      for (uint32_t i = 0; i < arrayLen; ++i) {
+        array[i] = types[i].get();
+      }
+
+      mSupportedNetworkTypes->SetAsArray(nsIDataType::VTYPE_WCHAR_STR,
+                                         nullptr,
+                                         arrayLen,
+                                         reinterpret_cast<void*>(array));
+      NS_Free(array);
+    }
+  }
 }
 
 void
 MobileConnectionChild::Shutdown()
 {
   if (mLive) {
     mLive = false;
     Send__delete__(this);
   }
 
   mListeners.Clear();
   mVoice = nullptr;
   mData = nullptr;
-}
-
-// nsIMobileConnection
-
-NS_IMETHODIMP
-MobileConnectionChild::GetServiceId(uint32_t* aServiceId)
-{
-  *aServiceId = mServiceId;
-  return NS_OK;
+  mSupportedNetworkTypes = nullptr;
 }
 
-NS_IMETHODIMP
+void
 MobileConnectionChild::RegisterListener(nsIMobileConnectionListener* aListener)
 {
-  NS_ENSURE_TRUE(!mListeners.Contains(aListener), NS_ERROR_UNEXPECTED);
-
-  mListeners.AppendObject(aListener);
-  return NS_OK;
+  if (!mListeners.Contains(aListener)) {
+    mListeners.AppendObject(aListener);
+  }
 }
 
-NS_IMETHODIMP
+void
 MobileConnectionChild::UnregisterListener(nsIMobileConnectionListener* aListener)
 {
-  NS_ENSURE_TRUE(mListeners.Contains(aListener), NS_ERROR_UNEXPECTED);
-
   mListeners.RemoveObject(aListener);
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-MobileConnectionChild::GetVoice(nsIMobileConnectionInfo** aVoice)
+MobileConnectionInfo*
+MobileConnectionChild::GetVoiceInfo()
 {
-  nsRefPtr<nsIMobileConnectionInfo> voice(mVoice);
-  voice.forget(aVoice);
-  return NS_OK;
+  return mVoice;
 }
 
-NS_IMETHODIMP
-MobileConnectionChild::GetData(nsIMobileConnectionInfo** aData)
+MobileConnectionInfo*
+MobileConnectionChild::GetDataInfo()
 {
-  nsRefPtr<nsIMobileConnectionInfo> data(mData);
-  data.forget(aData);
-  return NS_OK;
+  return mData;
 }
 
-NS_IMETHODIMP
+void
 MobileConnectionChild::GetIccId(nsAString& aIccId)
 {
   aIccId = mIccId;
-  return NS_OK;
 }
 
-NS_IMETHODIMP
+void
 MobileConnectionChild::GetRadioState(nsAString& aRadioState)
 {
   aRadioState = mRadioState;
-  return NS_OK;
+}
+
+nsIVariant*
+MobileConnectionChild::GetSupportedNetworkTypes()
+{
+  return mSupportedNetworkTypes;
 }
 
-NS_IMETHODIMP
-MobileConnectionChild::GetSupportedNetworkTypes(char16_t*** aTypes,
-                                                uint32_t* aLength)
+void
+MobileConnectionChild::GetLastNetwork(nsAString& aNetwork)
 {
-  NS_ENSURE_ARG(aTypes);
-  NS_ENSURE_ARG(aLength);
-
-  *aLength = mSupportedNetworkTypes.Length();
-  *aTypes =
-    static_cast<char16_t**>(nsMemory::Alloc((*aLength) * sizeof(char16_t*)));
-  NS_ENSURE_TRUE(*aTypes, NS_ERROR_OUT_OF_MEMORY);
-
-  for (uint32_t i = 0; i < *aLength; i++) {
-    (*aTypes)[i] = ToNewUnicode(mSupportedNetworkTypes[i]);
-  }
-
-  return NS_OK;
+  aNetwork = mLastNetwork;
 }
 
-NS_IMETHODIMP
-MobileConnectionChild::GetLastKnownNetwork(nsAString& aNetwork)
+void
+MobileConnectionChild::GetLastHomeNetwork(nsAString& aNetwork)
 {
-  aNetwork = mLastNetwork;
-  return NS_OK;
+  aNetwork = mLastHomeNetwork;
 }
 
-NS_IMETHODIMP
-MobileConnectionChild::GetLastKnownHomeNetwork(nsAString& aNetwork)
-{
-  aNetwork = mLastHomeNetwork;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
+void
 MobileConnectionChild::GetNetworkSelectionMode(nsAString& aMode)
 {
   aMode = mNetworkSelectionMode;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetNetworks(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetNetworksRequest(), aCallback) ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SelectNetwork(nsIMobileNetworkInfo* aNetwork,
-                                     nsIMobileConnectionCallback* aCallback)
-{
-  nsCOMPtr<nsIMobileNetworkInfo> network = aNetwork;
-  // We release the ref after serializing process is finished in
-  // MobileConnectionIPCSerializer.
-  return SendRequest(SelectNetworkRequest(network.forget().take()), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SelectNetworkAutomatically(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SelectNetworkAutoRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-
-NS_IMETHODIMP
-MobileConnectionChild::SetPreferredNetworkType(const nsAString& aType,
-                                               nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetPreferredNetworkTypeRequest(nsAutoString(aType)),
-                     aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetPreferredNetworkType(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetPreferredNetworkTypeRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetRoamingPreference(const nsAString& aMode,
-                                            nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetRoamingPreferenceRequest(nsAutoString(aMode)),
-                     aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetRoamingPreference(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetRoamingPreferenceRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetVoicePrivacyMode(bool aEnabled,
-                                           nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetVoicePrivacyModeRequest(aEnabled), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetVoicePrivacyMode(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetVoicePrivacyModeRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SendMMI(const nsAString& aMmi,
-                               nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SendMmiRequest(nsAutoString(aMmi)), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::CancelMMI(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(CancelMmiRequest(), aCallback) ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetCallForwarding(JS::Handle<JS::Value> aOptions,
-                                         nsIMobileConnectionCallback* aCallback)
-{
-  AutoSafeJSContext cx;
-  IPC::MozCallForwardingOptions options;
-  if(!options.Init(cx, aOptions)) {
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  return SendRequest(SetCallForwardingRequest(options), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetCallForwarding(uint16_t aReason,
-                                         nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetCallForwardingRequest(aReason), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetCallBarring(JS::Handle<JS::Value> aOptions,
-                                      nsIMobileConnectionCallback* aCallback)
-{
-  AutoSafeJSContext cx;
-  IPC::MozCallBarringOptions options;
-  if(!options.Init(cx, aOptions)) {
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  return SendRequest(SetCallBarringRequest(options), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetCallBarring(JS::Handle<JS::Value> aOptions,
-                                      nsIMobileConnectionCallback* aCallback)
-{
-  AutoSafeJSContext cx;
-  IPC::MozCallBarringOptions options;
-  if(!options.Init(cx, aOptions)) {
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  return SendRequest(GetCallBarringRequest(options), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::ChangeCallBarringPassword(JS::Handle<JS::Value> aOptions,
-                                                 nsIMobileConnectionCallback* aCallback)
-{
-  AutoSafeJSContext cx;
-  IPC::MozCallBarringOptions options;
-  if(!options.Init(cx, aOptions)) {
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  return SendRequest(ChangeCallBarringPasswordRequest(options), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetCallWaiting(bool aEnabled,
-                                      nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetCallWaitingRequest(aEnabled), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetCallWaiting(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetCallWaitingRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetCallingLineIdRestriction(uint16_t aMode,
-                                                   nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetCallingLineIdRestrictionRequest(aMode), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::GetCallingLineIdRestriction(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(GetCallingLineIdRestrictionRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::ExitEmergencyCbMode(nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(ExitEmergencyCbModeRequest(), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-MobileConnectionChild::SetRadioEnabled(bool aEnabled,
-                                       nsIMobileConnectionCallback* aCallback)
-{
-  return SendRequest(SetRadioEnabledRequest(aEnabled), aCallback)
-    ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
-MobileConnectionChild::SendRequest(const MobileConnectionRequest& aRequest,
-                                   nsIMobileConnectionCallback* aCallback)
+MobileConnectionChild::SendRequest(MobileConnectionRequest aRequest,
+                                   nsIMobileConnectionCallback* aRequestCallback)
 {
   NS_ENSURE_TRUE(mLive, false);
 
   // Deallocated in MobileConnectionChild::DeallocPMobileConnectionRequestChild().
-  MobileConnectionRequestChild* actor =
-    new MobileConnectionRequestChild(aCallback);
+  MobileConnectionRequestChild* actor = new MobileConnectionRequestChild(aRequestCallback);
   SendPMobileConnectionRequestConstructor(actor, aRequest);
 
   return true;
 }
 
 void
 MobileConnectionChild::ActorDestroy(ActorDestroyReason why)
 {
--- a/dom/mobileconnection/ipc/MobileConnectionChild.h
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.h
@@ -1,64 +1,93 @@
 /* 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_mobileconnection_MobileConnectionChild_h
 #define mozilla_dom_mobileconnection_MobileConnectionChild_h
 
+#include "mozilla/dom/MobileConnectionCallback.h"
 #include "mozilla/dom/MobileConnectionInfo.h"
-#include "mozilla/dom/mobileconnection/PMobileConnectionChild.h"
-#include "mozilla/dom/mobileconnection/PMobileConnectionRequestChild.h"
+#include "mozilla/dom/PMobileConnectionChild.h"
+#include "mozilla/dom/PMobileConnectionRequestChild.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIVariant.h"
 
-class nsIMobileConnectionCallback;
-
 namespace mozilla {
 namespace dom {
 namespace mobileconnection {
 
 /**
  * Child actor of PMobileConnection. The object is created by
  * MobileConnectionIPCService and destroyed after MobileConnectionIPCService is
  * shutdown. For multi-sim device, more than one instance will
  * be created and each instance represents a sim slot.
  */
-class MobileConnectionChild MOZ_FINAL : public PMobileConnectionChild
-                                      , public nsIMobileConnection
+class MobileConnectionChild : public PMobileConnectionChild
 {
+  NS_INLINE_DECL_REFCOUNTING(MobileConnectionChild)
+
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIMOBILECONNECTION
-
-  MobileConnectionChild(uint32_t aServiceId);
+  MobileConnectionChild()
+    : mLive(true)
+  {
+    MOZ_COUNT_CTOR(MobileConnectionChild);
+  }
 
   void
   Init();
 
   void
   Shutdown();
 
-private:
-  MobileConnectionChild() MOZ_DELETE;
+  void
+  RegisterListener(nsIMobileConnectionListener* aListener);
+
+  void
+  UnregisterListener(nsIMobileConnectionListener* aListener);
+
+  MobileConnectionInfo*
+  GetVoiceInfo();
+
+  MobileConnectionInfo*
+  GetDataInfo();
+
+  void
+  GetIccId(nsAString& aIccId);
+
+  void
+  GetRadioState(nsAString& aRadioState);
 
-  // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor
+  nsIVariant*
+  GetSupportedNetworkTypes();
+
+  void
+  GetLastNetwork(nsAString& aNetwork);
+
+  void
+  GetLastHomeNetwork(nsAString& aNetwork);
+
+  void
+  GetNetworkSelectionMode(nsAString& aMode);
+
+  bool
+  SendRequest(MobileConnectionRequest aRequest,
+              nsIMobileConnectionCallback* aRequestCallback);
+
+protected:
+  virtual
   ~MobileConnectionChild()
   {
     MOZ_COUNT_DTOR(MobileConnectionChild);
+    Shutdown();
   }
 
-protected:
-  bool
-  SendRequest(const MobileConnectionRequest& aRequest,
-              nsIMobileConnectionCallback* aCallback);
-
   virtual void
   ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual PMobileConnectionRequestChild*
   AllocPMobileConnectionRequestChild(const MobileConnectionRequest& request) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPMobileConnectionRequestChild(PMobileConnectionRequestChild* aActor) MOZ_OVERRIDE;
@@ -102,27 +131,26 @@ protected:
 
   virtual bool
   RecvNotifyLastHomeNetworkChanged(const nsString& aNetwork) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyNetworkSelectionModeChanged(const nsString& aMode) MOZ_OVERRIDE;
 
 private:
-  uint32_t mServiceId;
   bool mLive;
   nsCOMArray<nsIMobileConnectionListener> mListeners;
+  nsCOMPtr<nsIWritableVariant> mSupportedNetworkTypes;
   nsRefPtr<MobileConnectionInfo> mVoice;
   nsRefPtr<MobileConnectionInfo> mData;
   nsString mIccId;
   nsString mRadioState;
   nsString mLastNetwork;
   nsString mLastHomeNetwork;
   nsString mNetworkSelectionMode;
-  nsTArray<nsString> mSupportedNetworkTypes;
 };
 
 /******************************************************************************
  * PMobileConnectionRequestChild
  ******************************************************************************/
 
 /**
  * Child actor of PMobileConnectionRequest. The object is created when an
--- a/dom/mobileconnection/ipc/MobileConnectionIPCSerializer.h
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCSerializer.h
@@ -1,20 +1,20 @@
 /* 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_mobileconnection_MobileConnectionIPCSerialiser_h
-#define mozilla_dom_mobileconnection_MobileConnectionIPCSerialiser_h
+#ifndef dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
+#define dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/dom/MobileCellInfo.h"
 #include "mozilla/dom/MobileConnectionInfo.h"
 #include "mozilla/dom/MobileNetworkInfo.h"
-#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "MozMobileConnectionBinding.h"
 
 using mozilla::AutoSafeJSContext;
 using mozilla::dom::MobileNetworkInfo;
 using mozilla::dom::MobileCellInfo;
 using mozilla::dom::MobileConnectionInfo;
 
 typedef nsIMobileCellInfo* nsMobileCellInfo;
 typedef nsIMobileConnectionInfo* nsMobileConnectionInfo;
@@ -741,9 +741,9 @@ struct ParamTraits<MozCallBarringOptions
     }
 
     return true;
   }
 };
 
 } // namespace IPC
 
-#endif // mozilla_dom_mobileconnection_MobileConnectionIPCSerialiser_h
+#endif // dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
--- a/dom/mobileconnection/ipc/MobileConnectionIPCService.cpp
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCService.cpp
@@ -9,56 +9,386 @@
 #include "mozilla/StaticPtr.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobileconnection;
 
 NS_IMPL_ISUPPORTS(MobileConnectionIPCService, nsIMobileConnectionService)
 
+StaticRefPtr<MobileConnectionIPCService> sService;
+
+/* static */MobileConnectionIPCService*
+MobileConnectionIPCService::GetSingleton()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sService) {
+    return sService;
+  }
+
+  sService = new MobileConnectionIPCService();
+  return sService;
+}
+
 MobileConnectionIPCService::MobileConnectionIPCService()
 {
   int32_t numRil = Preferences::GetInt("ril.numRadioInterfaces", 1);
-  mItems.SetLength(numRil);
+  for (int32_t i = 0; i < numRil; i++) {
+    // Deallocated in ContentChild::DeallocPMobileConnectionChild().
+    nsRefPtr<MobileConnectionChild> client = new MobileConnectionChild();
+    NS_ASSERTION(client, "This shouldn't fail!");
+
+    ContentChild::GetSingleton()->SendPMobileConnectionConstructor(client, i);
+    client->Init();
+
+    mClients.AppendElement(client);
+  }
 }
 
 MobileConnectionIPCService::~MobileConnectionIPCService()
 {
-  uint32_t count = mItems.Length();
+  uint32_t count = mClients.Length();
   for (uint32_t i = 0; i < count; i++) {
-    if (mItems[i]) {
-      mItems[i]->Shutdown();
-    }
+    mClients[i]->Shutdown();
   }
+
+  mClients.Clear();
+}
+
+nsresult
+MobileConnectionIPCService::SendRequest(uint32_t aClientId,
+                                        MobileConnectionRequest aRequest,
+                                        nsIMobileConnectionCallback* aRequestCallback)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->SendRequest(aRequest, aRequestCallback);
+  return NS_OK;
 }
 
 // nsIMobileConnectionService
 
 NS_IMETHODIMP
-MobileConnectionIPCService::GetNumItems(uint32_t* aNumItems)
+MobileConnectionIPCService::RegisterListener(uint32_t aClientId,
+                                             nsIMobileConnectionListener* aListener)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->RegisterListener(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::UnregisterListener(uint32_t aClientId,
+                                               nsIMobileConnectionListener* aListener)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->UnregisterListener(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetLastKnownNetwork(uint32_t aClientId,
+                                                nsAString& aLastNetwork)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetLastNetwork(aLastNetwork);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetLastKnownHomeNetwork(uint32_t aClientId,
+                                                    nsAString& aLastNetwork)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetLastHomeNetwork(aLastNetwork);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetVoiceConnectionInfo(uint32_t aClientId,
+                                                   nsIMobileConnectionInfo** aInfo)
 {
-  *aNumItems = mItems.Length();
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIMobileConnectionInfo> info = mClients[aClientId]->GetVoiceInfo();
+  info.forget(aInfo);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetDataConnectionInfo(uint32_t aClientId,
+                                                  nsIMobileConnectionInfo** aInfo)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIMobileConnectionInfo> info = mClients[aClientId]->GetDataInfo();
+  info.forget(aInfo);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetIccId(uint32_t aClientId, nsAString& aIccId)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetIccId(aIccId);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetNetworkSelectionMode(uint32_t aClientId,
+                                                    nsAString& aNetworkSelectionMode)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetNetworkSelectionMode(aNetworkSelectionMode);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetRadioState(uint32_t aClientId,
+                                          nsAString& aRadioState)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetRadioState(aRadioState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetSupportedNetworkTypes(uint32_t aClientId,
+                                                     nsIVariant** aSupportedTypes)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIVariant> supportedTypes = mClients[aClientId]->GetSupportedNetworkTypes();
+  supportedTypes.forget(aSupportedTypes);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileConnectionIPCService::GetItemByServiceId(uint32_t aServiceId,
-                                               nsIMobileConnection** aItem)
+MobileConnectionIPCService::GetNetworks(uint32_t aClientId,
+                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetNetworksRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SelectNetwork(uint32_t aClientId,
+                                          nsIMobileNetworkInfo* aNetwork,
+                                          nsIMobileConnectionCallback* aRequest)
+{
+  nsCOMPtr<nsIMobileNetworkInfo> network = aNetwork;
+  // We release the ref after serializing process is finished in
+  // MobileConnectionIPCSerializer.
+  return SendRequest(aClientId, SelectNetworkRequest(network.forget().take()), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SelectNetworkAutomatically(uint32_t aClientId,
+                                                       nsIMobileConnectionCallback* aRequest)
 {
-  NS_ENSURE_TRUE(aServiceId < mItems.Length(), NS_ERROR_INVALID_ARG);
+  return SendRequest(aClientId, SelectNetworkAutoRequest(), aRequest);
+}
+
 
-  if (!mItems[aServiceId]) {
-    nsRefPtr<MobileConnectionChild> child = new MobileConnectionChild(aServiceId);
+NS_IMETHODIMP
+MobileConnectionIPCService::SetPreferredNetworkType(uint32_t aClientId,
+                                                    const nsAString& aType,
+                                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId,
+                     SetPreferredNetworkTypeRequest(nsAutoString(aType)),
+                     aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetPreferredNetworkType(uint32_t aClientId,
+                                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetPreferredNetworkTypeRequest(), aRequest);
+}
 
-    // |SendPMobileConnectionConstructor| adds another reference to the child
-    // actor and removes in |DeallocPMobileConnectionChild|.
-    ContentChild::GetSingleton()->SendPMobileConnectionConstructor(child,
-                                                                   aServiceId);
-    child->Init();
+NS_IMETHODIMP
+MobileConnectionIPCService::SetRoamingPreference(uint32_t aClientId,
+                                                 const nsAString& aMode,
+                                                 nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId,
+                     SetRoamingPreferenceRequest(nsAutoString(aMode)),
+                     aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetRoamingPreference(uint32_t aClientId,
+                                                 nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetRoamingPreferenceRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetVoicePrivacyMode(uint32_t aClientId,
+                                                bool aEnabled,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetVoicePrivacyModeRequest(aEnabled), aRequest);
+}
 
-    mItems[aServiceId] = child;
+NS_IMETHODIMP
+MobileConnectionIPCService::GetVoicePrivacyMode(uint32_t aClientId,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetVoicePrivacyModeRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SendMMI(uint32_t aClientId,
+                                    const nsAString& aMmi,
+                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SendMmiRequest(nsAutoString(aMmi)), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::CancelMMI(uint32_t aClientId,
+                                      nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, CancelMmiRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallForwarding(uint32_t aClientId,
+                                              JS::Handle<JS::Value> aOptions,
+                                              nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallForwardingOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
   }
 
-  nsRefPtr<nsIMobileConnection> item(mItems[aServiceId]);
-  item.forget(aItem);
+  return SendRequest(aClientId, SetCallForwardingRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallForwarding(uint32_t aClientId,
+                                              uint16_t aReason,
+                                              nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallForwardingRequest(aReason), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallBarring(uint32_t aClientId,
+                                           JS::Handle<JS::Value> aOptions,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, SetCallBarringRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallBarring(uint32_t aClientId,
+                                           JS::Handle<JS::Value> aOptions,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, GetCallBarringRequest(options), aRequest);
+}
 
-  return NS_OK;
+NS_IMETHODIMP
+MobileConnectionIPCService::ChangeCallBarringPassword(uint32_t aClientId,
+                                                      JS::Handle<JS::Value> aOptions,
+                                                      nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, ChangeCallBarringPasswordRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallWaiting(uint32_t aClientId,
+                                           bool aEnabled,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetCallWaitingRequest(aEnabled), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallWaiting(uint32_t aClientId,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallWaitingRequest(), aRequest);
 }
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallingLineIdRestriction(uint32_t aClientId,
+                                                        uint16_t aMode,
+                                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetCallingLineIdRestrictionRequest(aMode), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallingLineIdRestriction(uint32_t aClientId,
+                                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallingLineIdRestrictionRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::ExitEmergencyCbMode(uint32_t aClientId,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, ExitEmergencyCbModeRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetRadioEnabled(uint32_t aClientId,
+                                            bool aEnabled,
+                                            nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetRadioEnabledRequest(aEnabled), aRequest);
+}
--- a/dom/mobileconnection/ipc/MobileConnectionIPCService.h
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCService.h
@@ -1,35 +1,42 @@
 /* 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_mobileconnection_MobileConnectionIPCService_h
 #define mozilla_dom_mobileconnection_MobileConnectionIPCService_h
 
 #include "nsCOMPtr.h"
-#include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
+#include "MobileConnectionChild.h"
 #include "nsIMobileConnectionService.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobileconnection {
 
 class MobileConnectionIPCService MOZ_FINAL : public nsIMobileConnectionService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMOBILECONNECTIONSERVICE
 
+  static MobileConnectionIPCService*
+  GetSingleton();
+
+private:
   MobileConnectionIPCService();
 
-private:
-  // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor
   ~MobileConnectionIPCService();
 
-  nsTArray<nsRefPtr<MobileConnectionChild>> mItems;
+  /** Send request */
+  nsresult
+  SendRequest(uint32_t aClientId, MobileConnectionRequest aRequest,
+              nsIMobileConnectionCallback* aRequestCallback);
+
+  nsTArray<nsRefPtr<MobileConnectionChild>> mClients;
 };
 
-} // namespace mobileconnection
-} // namespace dom
-} // namespace mozilla
+} // name space mobileconnection
+} // name space dom
+} // name space mozilla
 
 #endif // mozilla_dom_mobileconnection_MobileConnectionIPCService_h
--- a/dom/mobileconnection/ipc/MobileConnectionParent.cpp
+++ b/dom/mobileconnection/ipc/MobileConnectionParent.cpp
@@ -1,49 +1,47 @@
 /* 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 "mozilla/dom/mobileconnection/MobileConnectionParent.h"
+#include "MobileConnectionParent.h"
 
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h"
+#include "mozilla/dom/MobileConnectionIPCSerializer.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "nsIVariant.h"
 #include "nsJSUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobileconnection;
 
 MobileConnectionParent::MobileConnectionParent(uint32_t aClientId)
-  : mLive(true)
+  : mClientId(aClientId)
+  , mLive(true)
 {
   MOZ_COUNT_CTOR(MobileConnectionParent);
 
-  nsCOMPtr<nsIMobileConnectionService> service =
-    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-  NS_ASSERTION(service, "This shouldn't fail!");
+  mService = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ASSERTION(mService, "This shouldn't fail!");
 
-  nsresult rv = service->GetItemByServiceId(aClientId,
-                                            getter_AddRefs(mMobileConnection));
-  if (NS_SUCCEEDED(rv) && mMobileConnection) {
-    mMobileConnection->RegisterListener(this);
+  if (mService) {
+    mService->RegisterListener(mClientId, this);
   }
 }
 
 void
 MobileConnectionParent::ActorDestroy(ActorDestroyReason why)
 {
   mLive = false;
-  if (mMobileConnection) {
-    mMobileConnection->UnregisterListener(this);
-    mMobileConnection = nullptr;
+  if (mService) {
+    mService->UnregisterListener(mClientId, this);
+    mService = nullptr;
   }
 }
 
 bool
 MobileConnectionParent::RecvPMobileConnectionRequestConstructor(PMobileConnectionRequestParent* aActor,
                                                                 const MobileConnectionRequest& aRequest)
 {
   MobileConnectionRequestParent* actor = static_cast<MobileConnectionRequestParent*>(aActor);
@@ -102,18 +100,17 @@ MobileConnectionParent::RecvPMobileConne
 
 PMobileConnectionRequestParent*
 MobileConnectionParent::AllocPMobileConnectionRequestParent(const MobileConnectionRequest& request)
 {
   if (!AssertAppProcessPermission(Manager(), "mobileconnection")) {
     return nullptr;
   }
 
-  MobileConnectionRequestParent* actor =
-    new MobileConnectionRequestParent(mMobileConnection);
+  MobileConnectionRequestParent* actor = new MobileConnectionRequestParent(mClientId);
   // Add an extra ref for IPDL. Will be released in
   // MobileConnectionParent::DeallocPMobileConnectionRequestParent().
   actor->AddRef();
   return actor;
 }
 
 bool
 MobileConnectionParent::DeallocPMobileConnectionRequestParent(PMobileConnectionRequestParent* aActor)
@@ -128,69 +125,77 @@ MobileConnectionParent::RecvInit(nsMobil
                                  nsMobileConnectionInfo* aData,
                                  nsString* aLastKnownNetwork,
                                  nsString* aLastKnownHomeNetwork,
                                  nsString* aIccId,
                                  nsString* aNetworkSelectionMode,
                                  nsString* aRadioState,
                                  nsTArray<nsString>* aSupportedNetworkTypes)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  NS_ENSURE_SUCCESS(mMobileConnection->GetVoice(aVoice), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetData(aData), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetLastKnownNetwork(*aLastKnownNetwork), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetLastKnownHomeNetwork(*aLastKnownHomeNetwork), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetIccId(*aIccId), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetNetworkSelectionMode(*aNetworkSelectionMode), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetRadioState(*aRadioState), false);
+  NS_ENSURE_SUCCESS(mService->GetVoiceConnectionInfo(mClientId, aVoice), false);
+  NS_ENSURE_SUCCESS(mService->GetDataConnectionInfo(mClientId, aData), false);
+  NS_ENSURE_SUCCESS(mService->GetLastKnownNetwork(mClientId, *aLastKnownNetwork), false);
+  NS_ENSURE_SUCCESS(mService->GetLastKnownHomeNetwork(mClientId, *aLastKnownHomeNetwork), false);
+  NS_ENSURE_SUCCESS(mService->GetIccId(mClientId, *aIccId), false);
+  NS_ENSURE_SUCCESS(mService->GetNetworkSelectionMode(mClientId, *aNetworkSelectionMode), false);
+  NS_ENSURE_SUCCESS(mService->GetRadioState(mClientId, *aRadioState), false);
 
-  char16_t** types = nullptr;
-  uint32_t length = 0;
+  nsCOMPtr<nsIVariant> variant;
+  mService->GetSupportedNetworkTypes(mClientId, getter_AddRefs(variant));
 
-  nsresult rv = mMobileConnection->GetSupportedNetworkTypes(&types, &length);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    nsDependentString type(types[i]);
-    aSupportedNetworkTypes->AppendElement(type);
+  uint16_t type;
+  nsIID iid;
+  uint32_t count;
+  void* data;
+  if (NS_FAILED(variant->GetAsArray(&type, &iid, &count, &data))) {
+    return false;
   }
 
-  NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, types);
+  // We expect the element type is wstring.
+  if (type == nsIDataType::VTYPE_WCHAR_STR) {
+    char16_t** rawArray = reinterpret_cast<char16_t**>(data);
+    for (uint32_t i = 0; i < count; ++i) {
+      nsDependentString networkType(rawArray[i]);
+      aSupportedNetworkTypes->AppendElement(networkType);
+    }
+  }
+  NS_Free(data);
 
   return true;
 }
 
 // nsIMobileConnectionListener
 
 NS_IMPL_ISUPPORTS(MobileConnectionParent, nsIMobileConnectionListener)
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyVoiceChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  rv = mMobileConnection->GetVoice(getter_AddRefs(info));
+  rv = mService->GetVoiceConnectionInfo(mClientId, getter_AddRefs(info));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We release the ref after serializing process is finished in
   // MobileConnectionIPCSerializer.
   return SendNotifyVoiceInfoChanged(info.forget().take()) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyDataChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  rv = mMobileConnection->GetData(getter_AddRefs(info));
+  rv = mService->GetDataConnectionInfo(mClientId, getter_AddRefs(info));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We release the ref after serializing process is finished in
   // MobileConnectionIPCSerializer.
   return SendNotifyDataInfoChanged(info.forget().take()) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
@@ -246,29 +251,29 @@ MobileConnectionParent::NotifyOtaStatusC
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyIccChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsAutoString iccId;
-  mMobileConnection->GetIccId(iccId);
+  mService->GetIccId(mClientId, iccId);
 
   return SendNotifyIccChanged(iccId) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyRadioStateChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsAutoString radioState;
-  rv = mMobileConnection->GetRadioState(radioState);
+  rv = mService->GetRadioState(mClientId, radioState);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SendNotifyRadioStateChanged(radioState) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyClirModeChanged(uint32_t aMode)
 {
@@ -279,264 +284,264 @@ MobileConnectionParent::NotifyClirModeCh
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyLastKnownNetworkChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsAutoString network;
-  rv = mMobileConnection->GetLastKnownNetwork(network);
+  rv = mService->GetLastKnownNetwork(mClientId, network);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SendNotifyLastNetworkChanged(network) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyLastKnownHomeNetworkChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsAutoString network;
-  rv = mMobileConnection->GetLastKnownHomeNetwork(network);
+  rv = mService->GetLastKnownHomeNetwork(mClientId, network);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SendNotifyLastHomeNetworkChanged(network) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyNetworkSelectionModeChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
   nsAutoString mode;
-  rv = mMobileConnection->GetNetworkSelectionMode(mode);
+  rv = mService->GetNetworkSelectionMode(mClientId, mode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SendNotifyNetworkSelectionModeChanged(mode) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 /******************************************************************************
  * PMobileConnectionRequestParent
  ******************************************************************************/
 
 void
 MobileConnectionRequestParent::ActorDestroy(ActorDestroyReason why)
 {
   mLive = false;
-  mMobileConnection = nullptr;
+  mService = nullptr;
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetNetworksRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetNetworks(this));
+  return NS_SUCCEEDED(mService->GetNetworks(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SelectNetworkRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
   // Use dont_AddRef here because this instances is already AddRef-ed in
   // MobileConnectionIPCSerializer.h
   nsCOMPtr<nsIMobileNetworkInfo> network = dont_AddRef(aRequest.network());
-  return NS_SUCCEEDED(mMobileConnection->SelectNetwork(network, this));
+  return NS_SUCCEEDED(mService->SelectNetwork(mClientId, network, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SelectNetworkAutoRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SelectNetworkAutomatically(this));
+  return NS_SUCCEEDED(mService->SelectNetworkAutomatically(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetPreferredNetworkTypeRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetPreferredNetworkType(aRequest.type(), this));
+  return NS_SUCCEEDED(mService->SetPreferredNetworkType(mClientId, aRequest.type(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetPreferredNetworkTypeRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetPreferredNetworkType(this));
+  return NS_SUCCEEDED(mService->GetPreferredNetworkType(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetRoamingPreferenceRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetRoamingPreference(aRequest.mode(), this));
+  return NS_SUCCEEDED(mService->SetRoamingPreference(mClientId, aRequest.mode(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetRoamingPreferenceRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetRoamingPreference(this));
+  return NS_SUCCEEDED(mService->GetRoamingPreference(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetVoicePrivacyModeRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetVoicePrivacyMode(aRequest.enabled(), this));
+  return NS_SUCCEEDED(mService->SetVoicePrivacyMode(mClientId, aRequest.enabled(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetVoicePrivacyModeRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetVoicePrivacyMode(this));
+  return NS_SUCCEEDED(mService->GetVoicePrivacyMode(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SendMmiRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SendMMI(aRequest.mmi(), this));
+  return NS_SUCCEEDED(mService->SendMMI(mClientId, aRequest.mmi(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const CancelMmiRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->CancelMMI(this));
+  return NS_SUCCEEDED(mService->CancelMMI(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetCallForwardingRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aRequest.options(), &options)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
-  return NS_SUCCEEDED(mMobileConnection->SetCallForwarding(options, this));
+  return NS_SUCCEEDED(mService->SetCallForwarding(mClientId, options, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetCallForwardingRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetCallForwarding(aRequest.reason(), this));
+  return NS_SUCCEEDED(mService->GetCallForwarding(mClientId, aRequest.reason(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetCallBarringRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aRequest.options(), &options)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
-  return NS_SUCCEEDED(mMobileConnection->SetCallBarring(options, this));
+  return NS_SUCCEEDED(mService->SetCallBarring(mClientId, options, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetCallBarringRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aRequest.options(), &options)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
-  return NS_SUCCEEDED(mMobileConnection->GetCallBarring(options, this));
+  return NS_SUCCEEDED(mService->GetCallBarring(mClientId, options, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const ChangeCallBarringPasswordRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aRequest.options(), &options)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
-  return NS_SUCCEEDED(mMobileConnection->ChangeCallBarringPassword(options, this));
+  return NS_SUCCEEDED(mService->ChangeCallBarringPassword(mClientId, options, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetCallWaitingRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetCallWaiting(aRequest.enabled(), this));
+  return NS_SUCCEEDED(mService->SetCallWaiting(mClientId, aRequest.enabled(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetCallWaitingRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetCallWaiting(this));
+  return NS_SUCCEEDED(mService->GetCallWaiting(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetCallingLineIdRestrictionRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetCallingLineIdRestriction(aRequest.mode(), this));
+  return NS_SUCCEEDED(mService->SetCallingLineIdRestriction(mClientId, aRequest.mode(), this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const GetCallingLineIdRestrictionRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->GetCallingLineIdRestriction(this));
+  return NS_SUCCEEDED(mService->GetCallingLineIdRestriction(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const ExitEmergencyCbModeRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->ExitEmergencyCbMode(this));
+  return NS_SUCCEEDED(mService->ExitEmergencyCbMode(mClientId, this));
 }
 
 bool
 MobileConnectionRequestParent::DoRequest(const SetRadioEnabledRequest& aRequest)
 {
-  NS_ENSURE_TRUE(mMobileConnection, false);
+  NS_ENSURE_TRUE(mService, false);
 
-  return NS_SUCCEEDED(mMobileConnection->SetRadioEnabled(aRequest.enabled(), this));
+  return NS_SUCCEEDED(mService->SetRadioEnabled(mClientId, aRequest.enabled(), this));
 }
 
 nsresult
 MobileConnectionRequestParent::SendReply(const MobileConnectionReply& aReply)
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   return Send__delete__(this, aReply) ? NS_OK : NS_ERROR_FAILURE;
--- a/dom/mobileconnection/ipc/MobileConnectionParent.h
+++ b/dom/mobileconnection/ipc/MobileConnectionParent.h
@@ -1,17 +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/. */
 
 #ifndef mozilla_dom_mobileconnection_MobileConnectionParent_h
 #define mozilla_dom_mobileconnection_MobileConnectionParent_h
 
-#include "mozilla/dom/mobileconnection/PMobileConnectionParent.h"
-#include "mozilla/dom/mobileconnection/PMobileConnectionRequestParent.h"
+#include "mozilla/dom/PMobileConnectionParent.h"
+#include "mozilla/dom/PMobileConnectionRequestParent.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobileconnection {
 
@@ -50,18 +50,19 @@ protected:
 
   virtual bool
   RecvInit(nsMobileConnectionInfo* aVoice, nsMobileConnectionInfo* aData,
            nsString* aLastKnownNetwork, nsString* aLastKnownHomeNetwork,
            nsString* aIccId, nsString* aNetworkSelectionMode,
            nsString* aRadioState, nsTArray<nsString>* aSupportedNetworkTypes) MOZ_OVERRIDE;
 
 private:
-  nsCOMPtr<nsIMobileConnection> mMobileConnection;
+  uint32_t mClientId;
   bool mLive;
+  nsCOMPtr<nsIMobileConnectionService> mService;
 };
 
 /******************************************************************************
  * PMobileConnectionRequestParent
  ******************************************************************************/
 
 /**
  * Parent actor of PMobileConnectionRequestParent. The object is created along
@@ -72,21 +73,24 @@ private:
  */
 class MobileConnectionRequestParent : public PMobileConnectionRequestParent
                                     , public nsIMobileConnectionCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMOBILECONNECTIONCALLBACK
 
-  explicit MobileConnectionRequestParent(nsIMobileConnection* aMobileConnection)
-    : mMobileConnection(aMobileConnection)
+  explicit MobileConnectionRequestParent(uint32_t aClientId)
+    : mClientId(aClientId)
     , mLive(true)
   {
     MOZ_COUNT_CTOR(MobileConnectionRequestParent);
+
+    mService = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+    NS_ASSERTION(mService, "This shouldn't fail!");
   }
 
   bool
   DoRequest(const GetNetworksRequest& aRequest);
 
   bool
   DoRequest(const SelectNetworkRequest& aRequest);
 
@@ -159,17 +163,18 @@ protected:
 
   virtual void
   ActorDestroy(ActorDestroyReason why);
 
   nsresult
   SendReply(const MobileConnectionReply& aReply);
 
 private:
-  nsCOMPtr<nsIMobileConnection> mMobileConnection;
+  uint32_t mClientId;
   bool mLive;
+  nsCOMPtr<nsIMobileConnectionService> mService;
 };
 
 } // namespace mobileconnection
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobileconnection_MobileConnectionParent_h
--- a/dom/mobileconnection/ipc/PMobileConnection.ipdl
+++ b/dom/mobileconnection/ipc/PMobileConnection.ipdl
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include protocol PMobileConnectionRequest;
 include PMobileConnectionTypes;
 
 namespace mozilla {
 namespace dom {
-namespace mobileconnection {
 
 sync protocol PMobileConnection
 {
   manager PContent;
   manages PMobileConnectionRequest;
 
 child:
   NotifyVoiceInfoChanged(nsMobileConnectionInfo aInfo);
@@ -180,11 +179,10 @@ union MobileConnectionRequest
   SetCallWaitingRequest;
   GetCallWaitingRequest;
   SetCallingLineIdRestrictionRequest;
   GetCallingLineIdRestrictionRequest;
   ExitEmergencyCbModeRequest;
   SetRadioEnabledRequest;
 };
 
-} // namespace mobileconnection
+} // namespace mozilla
 } // namespace dom
-} // namespace mozilla
--- a/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl
+++ b/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl
@@ -4,17 +4,16 @@
  * 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 protocol PMobileConnection;
 include PMobileConnectionTypes;
 
 namespace mozilla {
 namespace dom {
-namespace mobileconnection {
 
 protocol PMobileConnectionRequest
 {
   manager PMobileConnection;
 
 child:
   /**
    * Send when asynchronous request has completed.
@@ -95,11 +94,10 @@ union MobileConnectionReply
   MobileConnectionReplySuccessCallForwarding;
   MobileConnectionReplySuccessCallBarring;
   MobileConnectionReplySuccessClirStatus;
   // Error
   MobileConnectionReplyError;
   MobileConnectionReplyErrorMmi;
 };
 
-} // namespace mobileconnection
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobileconnection/ipc/PMobileConnectionTypes.ipdlh
+++ b/dom/mobileconnection/ipc/PMobileConnectionTypes.ipdlh
@@ -1,26 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* 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/. */
 
-using nsMobileConnectionInfo from "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h";
-using nsMobileNetworkInfo from "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h";
+using nsMobileConnectionInfo from "mozilla/dom/MobileConnectionIPCSerializer.h";
+using nsMobileNetworkInfo from "mozilla/dom/MobileConnectionIPCSerializer.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
-using struct IPC::MozCallForwardingOptions from "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h";
-using struct IPC::MozCallBarringOptions from "mozilla/dom/mobileconnection/MobileConnectionIPCSerializer.h";
+using struct IPC::MozCallForwardingOptions from "mozilla/dom/MobileConnectionIPCSerializer.h";
+using struct IPC::MozCallBarringOptions from "mozilla/dom/MobileConnectionIPCSerializer.h";
 
 namespace mozilla {
 namespace dom {
-namespace mobileconnection {
 
 union AdditionalInformation {
   void_t;
   uint16_t;
   nsString[];
   MozCallForwardingOptions[];
 };
 
-} // namespace mobileconnection
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobileconnection/moz.build
+++ b/dom/mobileconnection/moz.build
@@ -1,40 +1,35 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+DIRS += ['interfaces']
+
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 
 EXPORTS.mozilla.dom += [
     'DOMMMIError.h',
+    'ipc/MobileConnectionIPCSerializer.h',
     'MobileCellInfo.h',
     'MobileConnection.h',
     'MobileConnectionArray.h',
+    'MobileConnectionCallback.h',
     'MobileConnectionInfo.h',
     'MobileNetworkInfo.h',
 ]
 
 EXPORTS.mozilla.dom.mobileconnection += [
     'ipc/MobileConnectionChild.h',
-    'ipc/MobileConnectionIPCSerializer.h',
+    'ipc/MobileConnectionIPCService.h',
     'ipc/MobileConnectionParent.h',
 ]
 
-XPIDL_SOURCES += [
-    'interfaces/nsICellInfo.idl',
-    'interfaces/nsIMobileCellInfo.idl',
-    'interfaces/nsIMobileConnectionInfo.idl',
-    'interfaces/nsIMobileConnectionService.idl',
-    'interfaces/nsIMobileNetworkInfo.idl',
-    'interfaces/nsINeighboringCellInfo.idl',
-]
-
 SOURCES += [
     'DOMMMIError.cpp',
     'ipc/MobileConnectionChild.cpp',
     'ipc/MobileConnectionIPCService.cpp',
     'ipc/MobileConnectionParent.cpp',
     'MobileCellInfo.cpp',
     'MobileConnection.cpp',
     'MobileConnectionArray.cpp',
@@ -45,23 +40,18 @@ SOURCES += [
 
 IPDL_SOURCES += [
     'ipc/PMobileConnection.ipdl',
     'ipc/PMobileConnectionRequest.ipdl',
     'ipc/PMobileConnectionTypes.ipdlh',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
-    XPIDL_SOURCES += [
-        'gonk/nsIGonkMobileConnectionService.idl',
-    ]
     EXTRA_COMPONENTS += [
-        'gonk/MobileConnectionService.js',
-        'gonk/MobileConnectionService.manifest',
+        'gonk/MobileConnectionGonkService.js',
+        'gonk/MobileConnectionGonkService.manifest',
     ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
-
-XPIDL_MODULE = 'dom_mobileconnection'
--- a/dom/mobilemessage/MobileMessageManager.cpp
+++ b/dom/mobilemessage/MobileMessageManager.cpp
@@ -18,33 +18,20 @@
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIDOMMozMmsMessage.h"
 #include "nsIDOMMozSmsMessage.h"
 #include "nsIMmsService.h"
 #include "nsIMobileMessageCallback.h"
 #include "nsIMobileMessageDatabaseService.h"
-#include "nsIMobileMessageService.h"
 #include "nsIObserverService.h"
 #include "nsISmsService.h"
 #include "nsServiceManagerUtils.h" // For do_GetService()
 
-// Service instantiation
-#include "ipc/SmsIPCService.h"
-#include "MobileMessageService.h"
-#ifdef MOZ_WIDGET_ANDROID
-#include "android/MobileMessageDatabaseService.h"
-#include "android/SmsService.h"
-#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-#include "nsIRilMobileMessageDatabaseService.h"
-#include "gonk/SmsService.h"
-#endif
-#include "nsXULAppAPI.h" // For XRE_GetProcessType()
-
 #define RECEIVED_EVENT_NAME         NS_LITERAL_STRING("received")
 #define RETRIEVING_EVENT_NAME       NS_LITERAL_STRING("retrieving")
 #define SENDING_EVENT_NAME          NS_LITERAL_STRING("sending")
 #define SENT_EVENT_NAME             NS_LITERAL_STRING("sent")
 #define FAILED_EVENT_NAME           NS_LITERAL_STRING("failed")
 #define DELIVERY_SUCCESS_EVENT_NAME NS_LITERAL_STRING("deliverysuccess")
 #define DELIVERY_ERROR_EVENT_NAME   NS_LITERAL_STRING("deliveryerror")
 #define READ_SUCCESS_EVENT_NAME     NS_LITERAL_STRING("readsuccess")
@@ -694,67 +681,8 @@ MobileMessageManager::GetSmscAddress(con
     return nullptr;
   }
 
   return request.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
-
-already_AddRefed<nsISmsService>
-NS_CreateSmsService()
-{
-  nsCOMPtr<nsISmsService> smsService;
-
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    smsService = SmsIPCService::GetSingleton();
-  } else {
-#ifdef MOZ_WIDGET_ANDROID
-    smsService = new SmsService();
-#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    smsService = new SmsService();
-#endif
-  }
-
-  return smsService.forget();
-}
-
-already_AddRefed<nsIMobileMessageDatabaseService>
-NS_CreateMobileMessageDatabaseService()
-{
-  nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService;
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    mobileMessageDBService = SmsIPCService::GetSingleton();
-  } else {
-#ifdef MOZ_WIDGET_ANDROID
-    mobileMessageDBService = new MobileMessageDatabaseService();
-#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    mobileMessageDBService =
-      do_CreateInstance(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
-#endif
-  }
-
-  return mobileMessageDBService.forget();
-}
-
-already_AddRefed<nsIMmsService>
-NS_CreateMmsService()
-{
-  nsCOMPtr<nsIMmsService> mmsService;
-
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    mmsService = SmsIPCService::GetSingleton();
-  } else {
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    mmsService = do_CreateInstance("@mozilla.org/mms/rilmmsservice;1");
-#endif
-  }
-
-  return mmsService.forget();
-}
-
-already_AddRefed<nsIMobileMessageService>
-NS_CreateMobileMessageService()
-{
-  nsCOMPtr<nsIMobileMessageService> service = new MobileMessageService();
-  return service.forget();
-}
--- a/dom/mobilemessage/MobileMessageService.cpp
+++ b/dom/mobilemessage/MobileMessageService.cpp
@@ -9,16 +9,30 @@
 #include "DeletedMessageInfo.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS(MobileMessageService, nsIMobileMessageService)
 
+/* static */ StaticRefPtr<MobileMessageService> MobileMessageService::sSingleton;
+
+/* static */ already_AddRefed<MobileMessageService>
+MobileMessageService::GetInstance()
+{
+  if (!sSingleton) {
+    sSingleton = new MobileMessageService();
+    ClearOnShutdown(&sSingleton);
+  }
+
+  nsRefPtr<MobileMessageService> service = sSingleton.get();
+  return service.forget();
+}
+
 NS_IMETHODIMP
 MobileMessageService::CreateSmsMessage(int32_t aId,
                                        uint64_t aThreadId,
                                        const nsAString& aIccId,
                                        const nsAString& aDelivery,
                                        const nsAString& aDeliveryStatus,
                                        const nsAString& aSender,
                                        const nsAString& aReceiver,
--- a/dom/mobilemessage/MobileMessageService.h
+++ b/dom/mobilemessage/MobileMessageService.h
@@ -1,32 +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_MobileMessageService_h
 #define mozilla_dom_mobilemessage_MobileMessageService_h
 
-#include "mozilla/Attributes.h" // For MOZ_FINAL
 #include "nsIMobileMessageService.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 class MobileMessageService MOZ_FINAL : public nsIMobileMessageService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMOBILEMESSAGESERVICE
 
-  MobileMessageService() { MOZ_COUNT_CTOR(MobileMessageService); }
+  static already_AddRefed<MobileMessageService> GetInstance();
 
 private:
-  // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor
-  ~MobileMessageService() { MOZ_COUNT_DTOR(MobileMessageService); }
+  ~MobileMessageService() {}
+
+  static StaticRefPtr<MobileMessageService> sSingleton;
+
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_MobileMessageService_h
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/SmsServicesFactory.cpp
@@ -0,0 +1,77 @@
+/* -*- 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 "ipc/SmsIPCService.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "android/MobileMessageDatabaseService.h"
+#include "android/SmsService.h"
+#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+#include "gonk/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"
+
+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_WIDGET_ANDROID
+    smsService = new SmsService();
+#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+    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_WIDGET_ANDROID
+    mobileMessageDBService = new MobileMessageDatabaseService();
+#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+    mobileMessageDBService = do_GetService(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
+#endif
+  }
+
+  return mobileMessageDBService.forget();
+}
+
+/* static */ already_AddRefed<nsIMmsService>
+SmsServicesFactory::CreateMmsService()
+{
+  nsCOMPtr<nsIMmsService> mmsService;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    mmsService = new SmsIPCService();
+  } else {
+#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+    mmsService = do_CreateInstance(RIL_MMSSERVICE_CONTRACTID);
+#endif
+  }
+
+  return mmsService.forget();
+}
+
+} // namespace mobilemessage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/SmsServicesFactory.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_mobilemessage_SmsServicesFactory_h
+#define mozilla_dom_mobilemessage_SmsServicesFactory_h
+
+#include "nsCOMPtr.h"
+
+class nsISmsService;
+class nsIMmsService;
+class nsIMobileMessageDatabaseService;
+
+namespace mozilla {
+namespace dom {
+namespace mobilemessage {
+
+class SmsServicesFactory
+{
+public:
+  static already_AddRefed<nsISmsService> CreateSmsService();
+  static already_AddRefed<nsIMobileMessageDatabaseService> CreateMobileMessageDatabaseService();
+  static already_AddRefed<nsIMmsService> CreateMmsService();
+};
+
+} // namespace mobilemessage
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_mobilemessage_SmsServicesFactory_h
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -323,19 +323,18 @@ MmsConnection.prototype = {
   },
 
   /**
    * Return the roaming status of voice call.
    *
    * @return true if voice call is roaming.
    */
   isVoiceRoaming: function() {
-    let connection =
-      gMobileConnectionService.getItemByServiceId(this.serviceId);
-    let isRoaming = connection && connection.voice && connection.voice.roaming;
+    let voice = gMobileConnectionService.getVoiceConnectionInfo(this.serviceId);
+    let isRoaming = voice.roaming;
     if (DEBUG) debug("isVoiceRoaming = " + isRoaming);
     return isRoaming;
   },
 
   /**
    * Get phone number from iccInfo.
    *
    * If the icc card is gsm card, the phone number is in msisdn.
--- a/dom/mobilemessage/interfaces/nsIMmsService.idl
+++ b/dom/mobilemessage/interfaces/nsIMmsService.idl
@@ -1,14 +1,13 @@
 /* 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 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"
 %}
@@ -24,15 +23,8 @@ interface nsIMmsService : nsISupports
 
   void retrieve(in long id,
                 in nsIMobileMessageCallback request);
 
   void sendReadReport(in DOMString messageID,
                       in DOMString toAddress,
                       in DOMString iccId);
 };
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsIMmsService>
-NS_CreateMmsService();
-%}
--- a/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl
@@ -41,15 +41,8 @@ interface nsIMobileMessageDatabaseServic
 
   void markMessageRead(in long messageId,
                        in boolean value,
                        in boolean sendReadReport,
                        in nsIMobileMessageCallback request);
 
   nsICursorContinueCallback createThreadCursor(in nsIMobileMessageCursorCallback callback);
 };
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsIMobileMessageDatabaseService>
-NS_CreateMobileMessageDatabaseService();
-%}
--- a/dom/mobilemessage/interfaces/nsIMobileMessageService.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageService.idl
@@ -59,15 +59,8 @@ interface nsIMobileMessageService : nsIS
                                             in DOMString          aLastMessageType);
 
   nsIDeletedMessageInfo createDeletedMessageInfo(
     [array, size_is(msgCount)] in long messageIds,
     in uint32_t msgCount,
     [array, size_is(threadCount)] in unsigned long long threadIds,
     in uint32_t threadCount);
 };
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsIMobileMessageService>
-NS_CreateMobileMessageService();
-%}
--- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
+++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
@@ -29,23 +29,16 @@ interface nsIRilMobileMessageDatabaseRec
 interface nsIRilMobileMessageDatabaseConcatenationCallback : nsISupports
 {
   /**
    * |aCompleteMessage|: jsval: the completely concatenated message. Noted, this value might be null.
    */
   void notify(in nsresult aRv, in jsval aCompleteMessage);
 };
 
-%{C++
-#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CID \
-  { 0x29785f90, 0x6b5b, 0x11e2, { 0x92, 0x01, 0x3b, 0x28, 0x01, 0x70, 0xb2, 0xec } }
-#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID \
-  "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1"
-%}
-
 [scriptable, uuid(0b437a5c-a2bc-11e3-bd1b-dbb173eb35f8)]
 interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
 {
   /**
    * |aMessage| Object: should contain the following properties for internal use:
    *   - |type| DOMString: "sms" or "mms"
    *   - |timestamp| Number: the timestamp of received message
    *   - |iccId| DOMString: [optional] the ICC ID of the SIM for receiving
--- a/dom/mobilemessage/interfaces/nsISmsService.idl
+++ b/dom/mobilemessage/interfaces/nsISmsService.idl
@@ -28,15 +28,8 @@ interface nsISmsService : nsISupports
 
   boolean isSilentNumber(in DOMString number);
   void addSilentNumber(in DOMString number);
   void removeSilentNumber(in DOMString number);
 
   void getSmscAddress(in unsigned long serviceId,
                       in nsIMobileMessageCallback request);
 };
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsISmsService>
-NS_CreateSmsService();
-%}
--- a/dom/mobilemessage/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/ipc/SmsIPCService.cpp
@@ -27,19 +27,16 @@ const char* kObservedPrefs[] = {
   kPrefMmsDefaultServiceId,
   kPrefSmsDefaultServiceId,
   nullptr
 };
 
 // TODO: Bug 767082 - WebSMS: sSmsChild leaks at shutdown
 PSmsChild* gSmsChild;
 
-// SmsIPCService is owned by nsLayoutModule.
-SmsIPCService* sSingleton = nullptr;
-
 PSmsChild*
 GetSmsChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gSmsChild) {
     gSmsChild = ContentChild::GetSingleton()->SendPSmsConstructor();
 
@@ -100,41 +97,23 @@ getDefaultServiceId(const char* aPrefKey
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS(SmsIPCService,
                   nsISmsService,
                   nsIMmsService,
                   nsIMobileMessageDatabaseService,
                   nsIObserver)
 
-/* static */ already_AddRefed<SmsIPCService>
-SmsIPCService::GetSingleton()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sSingleton) {
-    sSingleton = new SmsIPCService();
-  }
-
-  nsRefPtr<SmsIPCService> service = sSingleton;
-  return service.forget();
-}
-
 SmsIPCService::SmsIPCService()
 {
   Preferences::AddStrongObservers(this, kObservedPrefs);
   mMmsDefaultServiceId = getDefaultServiceId(kPrefMmsDefaultServiceId);
   mSmsDefaultServiceId = getDefaultServiceId(kPrefSmsDefaultServiceId);
 }
 
-SmsIPCService::~SmsIPCService()
-{
-  sSingleton = nullptr;
-}
-
 /*
  * Implementation of nsIObserver.
  */
 
 NS_IMETHODIMP
 SmsIPCService::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const char16_t* aData)
--- a/dom/mobilemessage/ipc/SmsIPCService.h
+++ b/dom/mobilemessage/ipc/SmsIPCService.h
@@ -25,24 +25,20 @@ class SmsIPCService MOZ_FINAL : public n
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISMSSERVICE
   NS_DECL_NSIMMSSERVICE
   NS_DECL_NSIMOBILEMESSAGEDATABASESERVICE
   NS_DECL_NSIOBSERVER
 
-  static already_AddRefed<SmsIPCService>
-  GetSingleton();
+  SmsIPCService();
 
 private:
-  SmsIPCService();
-
-  // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor
-  ~SmsIPCService();
+  ~SmsIPCService() {}
 
   uint32_t mMmsDefaultServiceId;
   uint32_t mSmsDefaultServiceId;
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/moz.build
+++ b/dom/mobilemessage/moz.build
@@ -8,16 +8,18 @@ DIRS += ['interfaces']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
 
 EXPORTS.mozilla.dom.mobilemessage += [
     'Constants.h',            # Required by almost all cpp files
     'ipc/SmsChild.h',
     'ipc/SmsParent.h',
+    'MobileMessageService.h', # Required by nsLayoutModule.cpp
+    'SmsServicesFactory.h',   # Required by nsLayoutModule.cpp
     'Types.h',                # Required by IPDL SmsTypes.h
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     SOURCES += [
         'android/MobileMessageDatabaseService.cpp',
         'android/SmsService.cpp',
     ]
@@ -55,16 +57,17 @@ UNIFIED_SOURCES += [
     'ipc/SmsParent.cpp',
     'MmsMessage.cpp',
     'MobileMessageCallback.cpp',
     'MobileMessageCursorCallback.cpp',
     'MobileMessageManager.cpp',
     'MobileMessageService.cpp',
     'MobileMessageThread.cpp',
     'SmsMessage.cpp',
+    'SmsServicesFactory.cpp',
 ]
 
 IPDL_SOURCES += [
     'ipc/PMobileMessageCursor.ipdl',
     'ipc/PSms.ipdl',
     'ipc/PSmsRequest.ipdl',
     'ipc/SmsTypes.ipdlh',
 ]
--- a/dom/notification/ChromeNotifications.js
+++ b/dom/notification/ChromeNotifications.js
@@ -43,30 +43,37 @@ ChromeNotifications.prototype = {
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     cpmm.addMessageListener("Notification:GetAllCrossOrigin:Return:OK", this);
   },
 
   performResend: function(notifications) {
     let resentNotifications = 0;
 
     notifications.forEach(function(notification) {
+      let behavior;
+      try {
+        behavior = JSON.parse(notification.mozbehavior);
+      } catch(e) {
+        behavior = undefined;
+      }
       appNotifier.showAppNotification(
         notification.icon,
         notification.title,
         notification.body,
         null,
         {
           manifestURL: notification.origin,
           id: notification.alertName,
           dir: notification.dir,
           lang: notification.lang,
           tag: notification.tag,
           dbId: notification.id,
           timestamp: notification.timestamp,
-          data: notification.data
+          data: notification.data,
+          mozbehavior: behavior
         }
       );
       resentNotifications++;
     });
 
     try {
       this.resendCallback && this.resendCallback(resentNotifications);
     } catch (ex) {
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -56,30 +56,33 @@ public:
   NS_IMETHOD Handle(const nsAString& aID,
                     const nsAString& aTitle,
                     const nsAString& aDir,
                     const nsAString& aLang,
                     const nsAString& aBody,
                     const nsAString& aTag,
                     const nsAString& aIcon,
                     const nsAString& aData,
+                    const nsAString& aBehavior,
                     JSContext* aCx)
   {
     MOZ_ASSERT(!aID.IsEmpty());
 
     RootedDictionary<NotificationOptions> options(aCx);
     options.mDir = Notification::StringToDirection(nsString(aDir));
     options.mLang = aLang;
     options.mBody = aBody;
     options.mTag = aTag;
     options.mIcon = aIcon;
-    nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow,
-                                                                       aID,
-                                                                       aTitle,
-                                                                       options);
+    options.mMozbehavior.Init(aBehavior);
+    nsRefPtr<Notification> notification;
+    notification = Notification::CreateInternal(mWindow,
+                                                aID,
+                                                aTitle,
+                                                options);
     ErrorResult rv;
     notification->InitFromBase64(aCx, aData, rv);
     if (rv.Failed()) {
       return rv.ErrorCode();
     }
 
     notification->SetStoredState(true);
 
@@ -390,20 +393,20 @@ NotificationObserver::Observe(nsISupport
   }
 
   return NS_OK;
 }
 
 Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
                            NotificationDirection aDir, const nsAString& aLang,
                            const nsAString& aTag, const nsAString& aIconUrl,
-                           nsPIDOMWindow* aWindow)
+                           const NotificationBehavior& aBehavior, nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow),
     mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
-    mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false), mIsStored(false)
+    mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false), mIsStored(false)
 {
   nsAutoString alertName;
   DebugOnly<nsresult> rv = GetOrigin(GetOwner(), alertName);
   MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed");
 
   // Get the notification name that is unique per origin + tag/ID.
   // The name of the alert is of the form origin#tag/ID.
   alertName.Append('#');
@@ -469,26 +472,33 @@ Notification::Constructor(const GlobalOb
 
   nsString dataString;
   nsCOMPtr<nsIStructuredCloneContainer> scContainer;
   scContainer = notification->GetDataCloneContainer();
   if (scContainer) {
     scContainer->GetDataAsBase64(dataString);
   }
 
+  nsAutoString behavior;
+  if (!aOptions.mMozbehavior.ToJSON(behavior)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
   aRv = notificationStorage->Put(origin,
                                  id,
                                  aTitle,
                                  DirectionToString(aOptions.mDir),
                                  aOptions.mLang,
                                  aOptions.mBody,
                                  aOptions.mTag,
                                  aOptions.mIcon,
                                  alertName,
-                                 dataString);
+                                 dataString,
+                                 behavior);
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
   notification->SetStoredState(true);
 
   return notification.forget();
@@ -519,16 +529,17 @@ Notification::CreateInternal(nsPIDOMWind
 
   nsRefPtr<Notification> notification = new Notification(id,
                                                          aTitle,
                                                          aOptions.mBody,
                                                          aOptions.mDir,
                                                          aOptions.mLang,
                                                          aOptions.mTag,
                                                          aOptions.mIcon,
+                                                         aOptions.mMozbehavior,
                                                          aWindow);
   return notification.forget();
 }
 
 Notification::~Notification() {}
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Notification)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
@@ -567,32 +578,42 @@ Notification::ShowInternal()
     // We do not have permission to show a notification or alert service
     // is not available.
     DispatchTrustedEvent(NS_LITERAL_STRING("error"));
     return;
   }
 
   nsresult rv;
   nsAutoString absoluteUrl;
-  if (mIconUrl.Length() > 0) {
-    // Resolve image URL against document base URI.
-    nsIDocument* doc = GetOwner()->GetExtantDoc();
-    if (doc) {
-      nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
-      if (baseUri) {
+  nsAutoString soundUrl;
+  // Resolve image URL against document base URI.
+  nsIDocument* doc = GetOwner()->GetExtantDoc();
+  if (doc) {
+    nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
+    if (baseUri) {
+      if (mIconUrl.Length() > 0) {
         nsCOMPtr<nsIURI> srcUri;
         rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
                                                        mIconUrl, doc, baseUri);
         if (NS_SUCCEEDED(rv)) {
           nsAutoCString src;
           srcUri->GetSpec(src);
           absoluteUrl = NS_ConvertUTF8toUTF16(src);
         }
       }
-
+      if (mBehavior.mSoundFile.Length() > 0) {
+        nsCOMPtr<nsIURI> srcUri;
+        rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
+            mBehavior.mSoundFile, doc, baseUri);
+        if (NS_SUCCEEDED(rv)) {
+          nsAutoCString src;
+          srcUri->GetSpec(src);
+          soundUrl = NS_ConvertUTF8toUTF16(src);
+        }
+      }
     }
   }
 
   nsCOMPtr<nsIObserver> observer = new NotificationObserver(this);
 
   // mDataObjectContainer might be uninitialized here because the notification
   // was constructed with an undefined data property.
   nsString dataStr;
@@ -618,16 +639,18 @@ Notification::ShowInternal()
         ops.mTextClickable = true;
         ops.mManifestURL = manifestUrl;
         ops.mId = mAlertName;
         ops.mDbId = mID;
         ops.mDir = DirectionToString(mDir);
         ops.mLang = mLang;
         ops.mTag = mTag;
         ops.mData = dataStr;
+        ops.mMozbehavior = mBehavior;
+        ops.mMozbehavior.mSoundFile = soundUrl;
 
         if (!ToJSValue(cx, ops, &val)) {
           NS_WARNING("Converting dict to object failed!");
           return;
         }
 
         appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
                                          observer, val);
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -109,21 +109,22 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
 
   void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData, ErrorResult& aRv);
 
   void InitFromBase64(JSContext* aCx, const nsAString& aData, ErrorResult& aRv);
+
 protected:
   Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
                NotificationDirection aDir, const nsAString& aLang,
                const nsAString& aTag, const nsAString& aIconUrl,
-               nsPIDOMWindow* aWindow);
+               const NotificationBehavior& aBehavior, nsPIDOMWindow* aWindow);
 
   static already_AddRefed<Notification> CreateInternal(nsPIDOMWindow* aWindow,
                                                        const nsAString& aID,
                                                        const nsAString& aTitle,
                                                        const NotificationOptions& aOptions);
 
   void ShowInternal();
   void CloseInternal();
@@ -164,16 +165,17 @@ protected:
   nsString mID;
   nsString mTitle;
   nsString mBody;
   NotificationDirection mDir;
   nsString mLang;
   nsString mTag;
   nsString mIconUrl;
   nsCOMPtr<nsIStructuredCloneContainer> mDataObjectContainer;
+  NotificationBehavior mBehavior;
 
   // It's null until GetData is first called
   nsCOMPtr<nsIVariant> mData;
 
   nsString mAlertName;
 
   bool mIsClosed;
 
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -67,30 +67,31 @@ NotificationStorage.prototype = {
     if (DEBUG) debug("Topic: " + aTopic);
     if (aTopic == "xpcom-shutdown") {
       Services.obs.removeObserver(this, "xpcom-shutdown");
       this.unregisterListeners();
     }
   },
 
   put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
-                data) {
+                data, behavior) {
     if (DEBUG) { debug("PUT: " + id + ": " + title); }
     var notification = {
       id: id,
       title: title,
       dir: dir,
       lang: lang,
       body: body,
       tag: tag,
       icon: icon,
       alertName: alertName,
       timestamp: new Date().getTime(),
       origin: origin,
-      data: data
+      data: data,
+      behavior: behavior
     };
 
     this._notifications[id] = notification;
     if (tag) {
       if (!this._byTag[origin]) {
         this._byTag[origin] = {};
       }
 
@@ -200,17 +201,18 @@ NotificationStorage.prototype = {
       try {
         callback.handle(notification.id,
                         notification.title,
                         notification.dir,
                         notification.lang,
                         notification.body,
                         notification.tag,
                         notification.icon,
-                        notification.data);
+                        notification.data,
+                        notification.behavior);
       } catch (e) {
         if (DEBUG) { debug("Error calling callback handle: " + e); }
       }
     });
     try {
       callback.done();
     } catch (e) {
       if (DEBUG) { debug("Error calling callback done: " + e); }
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -49,18 +49,17 @@ this.PhoneNumberUtils = {
     // In Multi-sim, there is more than one client in 
     // iccProvider/mobileConnectionProvider. Each client represents a
     // icc/mobileConnection service. To maintain the backward compatibility with
     // single sim, we always use client 0 for now. Adding support for multiple
     // sim will be addressed in bug 926740, if needed.
     let clientId = 0;
 
     // Get network mcc
-    let connection = mobileConnection.getItemByServiceId(clientId);
-    let voice = connection && connection.voice;
+    let voice = mobileConnection.getVoiceConnectionInfo(clientId);
     if (voice && voice.network && voice.network.mcc) {
       mcc = voice.network.mcc;
     }
 
     // Get SIM mcc
     let iccInfo = icc.getIccInfo(clientId);
     if (!mcc && iccInfo && iccInfo.mcc) {
       mcc = iccInfo.mcc;
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -407,24 +407,24 @@ WifiGeoPositionProvider.prototype = {
   getMobileInfo: function() {
     LOG("getMobileInfo called");
     try {
       let radioService = Cc["@mozilla.org/ril;1"]
                     .getService(Ci.nsIRadioInterfaceLayer);
       let service = Cc["@mozilla.org/mobileconnection/mobileconnectionservice;1"]
                     .getService(Ci.nsIMobileConnectionService);
 
+      let numInterfaces = radioService.numRadioInterfaces;
       let result = [];
-      for (let i = 0; i < service.length; i++) {
-        LOG("Looking for SIM in slot:" + i + " of " + service.length);
-        let connection = service.getItemByServiceId(i);
-        let voice = connection && connection.voice;
-        let cell = voice && voice.cell;
-        let type = voice && voice.type;
-        let network = voice && voice.network;
+      for (let i = 0; i < numInterfaces; i++) {
+        LOG("Looking for SIM in slot:" + i + " of " + numInterfaces);
+        let voice = service.getVoiceConnectionInfo(i);
+        let cell = voice.cell;
+        let type = voice.type;
+        let network = voice.network;
 
         if (network && cell && type) {
           if (type === "gsm" || type === "gprs" || type === "edge") {
             type = "gsm";
           } else {
             type = "wcdma";
           }
           result.push({ radio: type,
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -518,24 +518,20 @@ GonkGPSGeolocationProvider::SetReference
 
     nsCOMPtr<nsIMobileConnectionService> service =
       do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
     if (!service) {
       NS_WARNING("Cannot get MobileConnectionService");
       return;
     }
 
-    nsCOMPtr<nsIMobileConnection> connection;
+    nsCOMPtr<nsIMobileConnectionInfo> voice;
     // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
     // MultiSIM configuration
-    service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
-    NS_ENSURE_TRUE_VOID(connection);
-
-    nsCOMPtr<nsIMobileConnectionInfo> voice;
-    connection->GetVoice(getter_AddRefs(voice));
+    service->GetVoiceConnectionInfo(0 /* Client Id */, getter_AddRefs(voice));
     if (voice) {
       nsCOMPtr<nsIMobileCellInfo> cell;
       voice->GetCell(getter_AddRefs(cell));
       if (cell) {
         int32_t lac;
         int64_t cid;
 
         cell->GetGsmLocationAreaCode(&lac);
@@ -946,38 +942,26 @@ GonkGPSGeolocationProvider::Observe(nsIS
       int32_t state;
       int32_t type;
       iface->GetState(&state);
       iface->GetType(&type);
       bool connected = (state == nsINetworkInterface::NETWORK_STATE_CONNECTED);
       bool roaming = false;
       int gpsNetworkType = ConvertToGpsNetworkType(type);
       if (gpsNetworkType >= 0) {
-        if (rilface) {
-          do {
-            nsCOMPtr<nsIMobileConnectionService> service =
-              do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-            if (!service) {
-              break;
-            }
-
-            nsCOMPtr<nsIMobileConnection> connection;
-            // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
-            // MultiSIM configuration
-            service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
-            if (!connection) {
-              break;
-            }
-
-            nsCOMPtr<nsIMobileConnectionInfo> voice;
-            connection->GetVoice(getter_AddRefs(voice));
-            if (voice) {
-              voice->GetRoaming(&roaming);
-            }
-          } while (0);
+        nsCOMPtr<nsIMobileConnectionService> service =
+          do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+        if (rilface && service) {
+          nsCOMPtr<nsIMobileConnectionInfo> voice;
+          // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
+          // MultiSIM configuration
+          service->GetVoiceConnectionInfo(0 /* Client Id */, getter_AddRefs(voice));
+          if (voice) {
+            voice->GetRoaming(&roaming);
+          }
         }
         mAGpsRilInterface->update_network_state(
           connected,
           gpsNetworkType,
           roaming,
           /* extra_info = */ nullptr);
       }
     }
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -854,19 +854,17 @@ NetworkManager.prototype = {
       }
     }
   },
 
   dunRetryTimes: 0,
   dunRetryTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
   setupDunConnection: function() {
     this.dunRetryTimer.cancel();
-    let connection =
-      gMobileConnectionService.getItemByServiceId(this._dataDefaultServiceId);
-    let data = connection && connection.data;
+    let data = gMobileConnectionService.getDataConnectionInfo(this._dataDefaultServiceId);
     if (data && data.state === "registered") {
       this.dunRetryTimes = 0;
       ril.setupDataCallByType("dun");
       this.dunConnectTimer.cancel();
       this.dunConnectTimer.
         initWithCallback(this.onDunConnectTimerTimeout.bind(this),
                          MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
       return;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -196,17 +196,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsISystemWorkerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyService",
                                    "@mozilla.org/telephony/telephonyservice;1",
                                    "nsIGonkTelephonyService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
-                                   "nsIGonkMobileConnectionService");
+                                   "nsIMobileConnectionGonkService");
 
 XPCOMUtils.defineLazyGetter(this, "WAP", function() {
   let wap = {};
   Cu.import("resource://gre/modules/WapPushManager.js", wap);
   return wap;
 });
 
 XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function() {
@@ -579,41 +579,41 @@ XPCOMUtils.defineLazyGetter(this, "gRadi
       numCards = numCards == null ? this._getNumCards() : numCards;
       if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
         return true;
       }
 
       return false;
     },
 
-    _isValidStateForSetRadioEnabled: function(radioState) {
+    _isValidStateForSetRadioEnabled: function(clientId) {
+      let radioState = gMobileConnectionService.getRadioState(clientId);
       return radioState == RIL.GECKO_RADIOSTATE_ENABLED ||
              radioState == RIL.GECKO_RADIOSTATE_DISABLED;
     },
 
-    _isDummyForSetRadioEnabled: function(radioState, data) {
+    _isDummyForSetRadioEnabled: function(clientId, data) {
+      let radioState = gMobileConnectionService.getRadioState(clientId);
       return (radioState == RIL.GECKO_RADIOSTATE_ENABLED && data.enabled) ||
              (radioState == RIL.GECKO_RADIOSTATE_DISABLED && !data.enabled);
     },
 
     _handleMessage: function(message) {
       if (DEBUG) debug("RadioControl: handleMessage: " + JSON.stringify(message));
       let clientId = message.clientId || 0;
-      let connection =
-        gMobileConnectionService.getItemByServiceId(clientId);
-      let radioState = connection && connection.radioState;
-
-      if (!this._isValidStateForSetRadioEnabled(radioState)) {
+      let radioInterface = _ril.getRadioInterface(clientId);
+
+      if (!this._isValidStateForSetRadioEnabled(clientId)) {
         message.data.errorMsg = "InvalidStateError";
         message.callback(message.data);
         this._processNextMessage();
         return;
       }
 
-      if (this._isDummyForSetRadioEnabled(radioState, message.data)) {
+      if (this._isDummyForSetRadioEnabled(clientId, message.data)) {
         message.callback(message.data);
         this._processNextMessage();
         return;
       }
 
       if (message.data.enabled) {
         if (this._isRadioAbleToEnableAtClient(clientId)) {
           this._setRadioEnabledInternal(message);
@@ -1397,22 +1397,19 @@ DataConnectionHandler.prototype = {
     let networkInterface = this.dataNetworkInterfaces.get("default");
     if (!networkInterface) {
       if (DEBUG) {
         this.debug("No network interface for default data.");
       }
       return;
     }
 
-    let connection =
-      gMobileConnectionService.getItemByServiceId(this.clientId);
-
     // This check avoids data call connection if the radio is not ready
     // yet after toggling off airplane mode.
-    let radioState = connection && connection.radioState;
+    let radioState = gMobileConnectionService.getRadioState(this.clientId);
     if (radioState != RIL.GECKO_RADIOSTATE_ENABLED) {
       if (DEBUG) {
         this.debug("RIL is not ready for data connection: radio's not ready");
       }
       return;
     }
 
     // We only watch at "ril.data.enabled" flag changes for connecting or
@@ -1422,22 +1419,20 @@ DataConnectionHandler.prototype = {
     // the new values and reconnect the data call.
     if (this.dataCallSettings.oldEnabled === this.dataCallSettings.enabled) {
       if (DEBUG) {
         this.debug("No changes for ril.data.enabled flag. Nothing to do.");
       }
       return;
     }
 
-    let dataInfo = connection && connection.data;
+    let dataInfo = gMobileConnectionService.getDataConnectionInfo(this.clientId);
     let isRegistered =
-      dataInfo &&
       dataInfo.state == RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED;
     let haveDataConnection =
-      dataInfo &&
       dataInfo.type != RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN;
     if (!isRegistered || !haveDataConnection) {
       if (DEBUG) {
         this.debug("RIL is not ready for data connection: Phone's not " +
                    "registered or doesn't have data connection.");
       }
       return;
     }
@@ -1487,17 +1482,19 @@ DataConnectionHandler.prototype = {
       }
       return;
     }
     if (this._pendingApnSettings) {
       if (DEBUG) this.debug("We're changing apn settings, ignore any changes.");
       return;
     }
 
-    if (gRadioEnabledController.isDeactivatingDataCalls()) {
+    if (gRadioEnabledController.isDeactivatingDataCalls() ||
+        radioState == RIL.GECKO_RADIOSTATE_ENABLING ||
+        radioState == RIL.GECKO_RADIOSTATE_DISABLING) {
       // We're changing the radio power currently, ignore any changes.
       return;
     }
 
     if (DEBUG) {
       this.debug("Data call settings: connect data call.");
     }
     networkInterface.connect();
@@ -3000,18 +2997,18 @@ RadioInterface.prototype = {
     }
 
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when iccInfo has changed.
     gMessageManager.sendIccMessage("RIL:IccInfoChanged",
                                    this.clientId,
                                    message.iccid ? message : null);
 
-    // In bug 864489, icc related code will be move to gonk IccProvider, we may
-    // need a better way to notify icc change to MobileConnectionService.
+    // In bug 864489, icc related code will be move to IccGonkProvider, we may
+    // need a better way to notify icc change to MobileConnectionGonkProvider.
     gMobileConnectionService.notifyIccChanged(this.clientId,
                                               message.iccid || null);
 
     // Update lastKnownSimMcc.
     if (message.mcc) {
       try {
         Services.prefs.setCharPref("ril.lastKnownSimMcc",
                                    message.mcc.toString());
@@ -3700,28 +3697,25 @@ RadioInterface.prototype = {
         Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
         return;
       }
 
       if (!silent) {
         Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
       }
 
-      let connection =
-        gMobileConnectionService.getItemByServiceId(this.clientId);
       // If the radio is disabled or the SIM card is not ready, just directly
       // return with the corresponding error code.
       let errorCode;
-      let radioState = connection && connection.radioState;
+      let radioState = gMobileConnectionService.getRadioState(this.clientId);
       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 (radioState == null ||
-                 radioState == RIL.GECKO_RADIOSTATE_DISABLED) {
+      } else if (radioState == RIL.GECKO_RADIOSTATE_DISABLED) {
         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) {
@@ -4215,21 +4209,19 @@ DataCall.prototype = {
   },
 
   setup: function() {
     if (DEBUG) {
       this.debug("Going to set up data connection with APN " +
                  this.apnProfile.apn);
     }
 
-    let connection =
-      gMobileConnectionService.getItemByServiceId(this.clientId);
-    let dataInfo = connection && connection.data;
-    if (dataInfo == null ||
-        dataInfo.state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED ||
+    let radioInterface = this.gRIL.getRadioInterface(this.clientId);
+    let dataInfo = gMobileConnectionService.getDataConnectionInfo(this.clientId);
+    if (dataInfo.state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED ||
         dataInfo.type == RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN) {
       return;
     }
 
     let radioTechType = dataInfo.type;
     let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
     let authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(this.apnProfile.authtype);
     // Use the default authType if the value in database is invalid.
@@ -4248,18 +4240,16 @@ DataCall.prototype = {
       if (RIL.RIL_DATACALL_PDP_TYPES.indexOf(pdpType) < 0) {
         if (DEBUG) {
           this.debug("Invalid pdpType '" + pdpType + "', using '" +
                      RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT + "'");
         }
         pdpType = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT;
       }
     }
-
-    let radioInterface = this.gRIL.getRadioInterface(this.clientId);
     radioInterface.sendWorkerMessage("setupDataCall", {
       radioTech: radioTechnology,
       apn: this.apnProfile.apn,
       user: this.apnProfile.user,
       passwd: this.apnProfile.password,
       chappap: authType,
       pdptype: pdpType
     });
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -21,23 +21,16 @@
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
 #include "CallsList.h"
 #include "TelephonyCall.h"
 #include "TelephonyCallGroup.h"
 #include "TelephonyCallId.h"
 
-// Service instantiation
-#include "ipc/TelephonyIPCService.h"
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-#include "nsIGonkTelephonyService.h"
-#endif
-#include "nsXULAppAPI.h" // For XRE_GetProcessType()
-
 using namespace mozilla::dom;
 using mozilla::ErrorResult;
 
 class Telephony::Listener : public nsITelephonyListener
 {
   Telephony* mTelephony;
 
   virtual ~Listener() {}
@@ -740,24 +733,8 @@ Telephony::EnqueueEnumerationAck(const n
     return;
   }
 
   nsCOMPtr<nsIRunnable> task = new EnumerationAck(this, aType);
   if (NS_FAILED(NS_DispatchToCurrentThread(task))) {
     NS_WARNING("Failed to dispatch to current thread!");
   }
 }
-
-already_AddRefed<nsITelephonyService>
-NS_CreateTelephonyService()
-{
-  nsCOMPtr<nsITelephonyService> service;
-
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    service = new mozilla::dom::telephony::TelephonyIPCService();
-  } else {
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    service = do_CreateInstance(GONK_TELEPHONY_SERVICE_CONTRACTID);
-#endif
-  }
-
-  return service.forget();
-}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/TelephonyFactory.cpp
@@ -0,0 +1,30 @@
+/* -*- 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 "mozilla/dom/telephony/TelephonyFactory.h"
+#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+#include "nsIGonkTelephonyService.h"
+#endif
+#include "nsServiceManagerUtils.h"
+#include "nsXULAppAPI.h"
+#include "ipc/TelephonyIPCService.h"
+
+USING_TELEPHONY_NAMESPACE
+
+/* static */ already_AddRefed<nsITelephonyService>
+TelephonyFactory::CreateTelephonyService()
+{
+  nsCOMPtr<nsITelephonyService> service;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    service = new TelephonyIPCService();
+#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+  } else {
+    service = do_CreateInstance(GONK_TELEPHONY_SERVICE_CONTRACTID);
+#endif
+  }
+
+  return service.forget();
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/TelephonyFactory.h
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_telephony_TelephonyFactory_h
+#define mozilla_dom_telephony_TelephonyFactory_h
+
+#include "nsCOMPtr.h"
+#include "mozilla/dom/telephony/TelephonyCommon.h"
+
+class nsITelephonyService;
+
+BEGIN_TELEPHONY_NAMESPACE
+
+class TelephonyFactory
+{
+public:
+  static already_AddRefed<nsITelephonyService> CreateTelephonyService();
+};
+
+END_TELEPHONY_NAMESPACE
+
+#endif // mozilla_dom_telephony_TelephonyFactory_h
--- a/dom/telephony/moz.build
+++ b/dom/telephony/moz.build
@@ -17,27 +17,29 @@ EXPORTS.mozilla.dom += [
     'TelephonyCallGroup.h',
     'TelephonyCallId.h',
 ]
 
 EXPORTS.mozilla.dom.telephony += [
     'ipc/TelephonyChild.h',
     'ipc/TelephonyParent.h',
     'TelephonyCommon.h',
+    'TelephonyFactory.h',
 ]
 
 UNIFIED_SOURCES += [
     'CallsList.cpp',
     'ipc/TelephonyChild.cpp',
     'ipc/TelephonyIPCService.cpp',
     'ipc/TelephonyParent.cpp',
     'Telephony.cpp',
     'TelephonyCall.cpp',
     'TelephonyCallGroup.cpp',
     'TelephonyCallId.cpp',
+    'TelephonyFactory.cpp',
 ]
 
 IPDL_SOURCES += [
     'ipc/PTelephony.ipdl',
     'ipc/PTelephonyRequest.ipdl',
     'ipc/TelephonyTypes.ipdlh'
 ]
 
--- a/dom/telephony/nsITelephonyService.idl
+++ b/dom/telephony/nsITelephonyService.idl
@@ -260,15 +260,8 @@ interface nsITelephonyService : nsISuppo
   void conferenceCall(in unsigned long clientId);
   void separateCall(in unsigned long clientId, in unsigned long callIndex);
   void holdConference(in unsigned long clientId);
   void resumeConference(in unsigned long clientId);
 
   attribute bool microphoneMuted;
   attribute bool speakerEnabled;
 };
-
-%{C++
-template<typename T> struct already_AddRefed;
-
-already_AddRefed<nsITelephonyService>
-NS_CreateTelephonyService();
-%}
--- a/dom/webidl/AppNotificationServiceOptions.webidl
+++ b/dom/webidl/AppNotificationServiceOptions.webidl
@@ -10,9 +10,10 @@ dictionary AppNotificationServiceOptions
   boolean textClickable = false;
   DOMString manifestURL = "";
   DOMString id = "";
   DOMString dbId = "";
   DOMString dir = "";
   DOMString lang = "";
   DOMString tag = "";
   DOMString data = "";
+  NotificationBehavior mozbehavior = null;
 };
--- a/dom/webidl/MozIccManager.webidl
+++ b/dom/webidl/MozIccManager.webidl
@@ -97,16 +97,19 @@ interface MozIccManager : EventTarget
   const unsigned short STK_RESULT_NETWORK_CRNTLY_UNABLE_TO_PROCESS  = 0x21;
 
   /** User did not accept the proactive command */
   const unsigned short STK_RESULT_USER_NOT_ACCEPT                   = 0x22;
 
   /** User cleared down call before connection or network release */
   const unsigned short STK_RESULT_USER_CLEAR_DOWN_CALL              = 0x23;
 
+  /** Action in contradiction with the current timer state */
+  const unsigned short STK_RESULT_ACTION_CONTRADICTION_TIMER_STATE  = 0x24;
+
   /** Launch browser generic error code */
   const unsigned short STK_RESULT_LAUNCH_BROWSER_ERROR              = 0x26;
 
   /**
    * Results '3X' indicate that it is not worth the UICC re-trying with an
    * identical command, as it will only get the same response. However, the
    * decision to retry lies with the application.
    */
--- a/dom/webidl/MozNFC.webidl
+++ b/dom/webidl/MozNFC.webidl
@@ -8,41 +8,47 @@
 interface MozNFCManager {
    /**
     * API to check if the given application's manifest
     * URL is registered with the Chrome Process or not.
     *
     * Returns success if given manifestUrl is registered for 'onpeerready',
     * otherwise error
     */
+   [CheckPermissions="nfc-manager"]
    DOMRequest checkP2PRegistration(DOMString manifestUrl);
 
    /**
     * Notify that user has accepted to share nfc message on P2P UI
     */
+   [CheckPermissions="nfc-manager"]
    void notifyUserAcceptedP2P(DOMString manifestUrl);
 
    /**
     * Notify the status of sendFile operation
     */
+   [CheckPermissions="nfc-manager"]
    void notifySendFileStatus(octet status, DOMString requestId);
 
    /**
     * Power on the NFC hardware and start polling for NFC tags or devices.
     */
+   [CheckPermissions="nfc-manager"]
    DOMRequest startPoll();
 
    /**
     * Stop polling for NFC tags or devices. i.e. enter low power mode.
     */
+   [CheckPermissions="nfc-manager"]
    DOMRequest stopPoll();
 
    /**
     * Power off the NFC hardware.
     */
+   [CheckPermissions="nfc-manager"]
    DOMRequest powerOff();
 };
 
 [JSImplementation="@mozilla.org/navigatorNfc;1",
  NavigatorProperty="mozNfc",
  Func="Navigator::HasNFCSupport",
  CheckPermissions="nfc-read nfc-write",
  AvailableIn="CertifiedApps"]
--- a/dom/webidl/Notification.webidl
+++ b/dom/webidl/Notification.webidl
@@ -57,22 +57,30 @@ interface Notification : EventTarget {
 
 dictionary NotificationOptions {
   NotificationDirection dir = "auto";
   DOMString lang = "";
   DOMString body = "";
   DOMString tag = "";
   DOMString icon = "";
   any data = null;
+  NotificationBehavior mozbehavior = null;
 };
 
 dictionary GetNotificationOptions {
   DOMString tag;
 };
 
+dictionary NotificationBehavior {
+  boolean noscreen = false;
+  boolean noclear = false;
+  DOMString soundFile = "";
+  sequence<unsigned long> vibrationPattern;
+};
+
 enum NotificationPermission {
   "default",
   "denied",
   "granted"
 };
 
 callback NotificationPermissionCallback = void (NotificationPermission permission);
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -120,16 +120,25 @@ using mozilla::dom::bluetooth::Bluetooth
 
 #ifdef MOZ_WIDGET_GONK
 #include "AudioManager.h"
 using mozilla::dom::gonk::AudioManager;
 #include "nsVolumeService.h"
 using mozilla::system::nsVolumeService;
 #endif
 
+#ifdef MOZ_B2G_RIL
+#include "nsIMobileConnectionService.h"
+#include "mozilla/dom/mobileconnection/MobileConnectionIPCService.h"
+using mozilla::dom::mobileconnection::MobileConnectionIPCService;
+#ifdef MOZ_WIDGET_GONK
+#include "nsIMobileConnectionGonkService.h"
+#endif
+#endif
+
 #include "AudioChannelAgent.h"
 using mozilla::dom::AudioChannelAgent;
 
 // Editor stuff
 #include "nsEditorCID.h"
 #include "nsEditor.h"
 #include "nsPlaintextEditor.h"
 #include "nsEditorController.h" //CID
@@ -211,44 +220,48 @@ static void Shutdown();
 #include "nsDeviceSensors.h"
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 #include "nsCSPService.h"
 #include "nsCSPContext.h"
 #include "nsISmsService.h"
 #include "nsIMmsService.h"
-#include "nsIMobileConnectionService.h"
 #include "nsIMobileMessageService.h"
 #include "nsIMobileMessageDatabaseService.h"
+#include "mozilla/dom/mobilemessage/MobileMessageService.h"
+#include "mozilla/dom/mobilemessage/SmsServicesFactory.h"
 #include "nsIPowerManagerService.h"
 #include "nsIAlarmHalService.h"
 #include "nsIMediaManager.h"
 #include "nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/DataStoreService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
 #include "StreamingProtocolService.h"
 
+#include "mozilla/dom/telephony/TelephonyFactory.h"
 #include "nsITelephonyService.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "GonkGPSGeolocationProvider.h"
 #endif
 #include "MediaManager.h"
 
 #include "GMPService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::mobilemessage;
+using namespace mozilla::dom::telephony;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::workers::ServiceWorkerManager;
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::TCPSocketParent;
 using mozilla::dom::TCPServerSocketChild;
 using mozilla::dom::UDPSocketChild;
@@ -312,22 +325,24 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(AudioChan
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceSensors)
 
 #ifndef MOZ_WIDGET_GONK
 #if defined(ANDROID)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHapticFeedback)
 #endif
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ThirdPartyUtil, Init)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISmsService, NS_CreateSmsService)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMmsService, NS_CreateMmsService)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISmsService,
+                                         SmsServicesFactory::CreateSmsService)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMmsService,
+                                         SmsServicesFactory::CreateMmsService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageService,
-                                         NS_CreateMobileMessageService)
+                                         MobileMessageService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageDatabaseService,
-                                         NS_CreateMobileMessageDatabaseService)
+                                         SmsServicesFactory::CreateMobileMessageDatabaseService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPowerManagerService,
                                          PowerManagerService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIAlarmHalService,
                                          AlarmHalService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITimeService,
                                          TimeService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIStreamingProtocolControllerService,
                                          StreamingProtocolControllerService::GetInstance)
@@ -343,20 +358,18 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
                                          GonkGPSGeolocationProvider::GetSingleton)
 // Since the nsVolumeService constructor calls into nsIPowerManagerService,
 // we need it to be constructed sometime after nsIPowerManagerService.
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsVolumeService,
                                          nsVolumeService::GetSingleton)
 #endif
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMediaManagerService,
                                          MediaManager::GetInstance)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileConnectionService,
-                                         NS_CreateMobileConnectionService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelephonyService,
-                                         NS_CreateTelephonyService)
+                                         TelephonyFactory::CreateTelephonyService)
 
 //-----------------------------------------------------------------------------
 
 static bool gInitialized = false;
 
 // Perform our one-time intialization for this module
 
 // static
@@ -788,17 +801,19 @@ NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTR
 
 #ifdef ACCESSIBILITY
 NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(TELEPHONY_SERVICE_CID);
 
 NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID);
 
+#ifdef MOZ_B2G_RIL
 NS_DEFINE_NAMED_CID(NS_MOBILE_CONNECTION_SERVICE_CID);
+#endif
 
 static nsresult
 CreateWindowCommandTableConstructor(nsISupports *aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIControllerCommandTable> commandTable =
       do_CreateInstance(NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &rv);
@@ -918,16 +933,41 @@ nsEditingCommandTableConstructor(nsISupp
   if (NS_FAILED(rv)) return rv;
 
   // we don't know here whether we're being created as an instance,
   // or a service, so we can't become immutable
 
   return commandTable->QueryInterface(aIID, aResult);
 }
 
+#ifdef MOZ_B2G_RIL
+
+static nsresult
+nsIMobileConnectionServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
+                                      void **aResult)
+{
+  nsCOMPtr<nsIMobileConnectionService> service;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    service = MobileConnectionIPCService::GetSingleton();
+  } else {
+#ifdef MOZ_WIDGET_GONK
+    service = do_CreateInstance(NS_MOBILECONNECTION_GONK_SERVICE_CONTRACTID);
+#endif
+  }
+
+  if (!service) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return service->QueryInterface(aIID, aResult);
+}
+
+#endif
+
 static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
   XPCONNECT_CIDENTRIES
 #ifdef DEBUG
   { &kNS_FRAME_UTIL_CID, false, nullptr, CreateNewFrameUtil },
   { &kNS_LAYOUT_DEBUGGER_CID, false, nullptr, CreateNewLayoutDebugger },
 #endif
   { &kNS_FRAMETRAVERSAL_CID, false, nullptr, CreateNewFrameTraversal },
   { &kNS_BOXOBJECT_CID, false, nullptr, CreateNewBoxObject },
@@ -1073,17 +1113,19 @@ static const mozilla::Module::CIDEntry k
   { &kNS_MEDIAMANAGERSERVICE_CID, false, nullptr, nsIMediaManagerServiceConstructor },
 #ifdef MOZ_GAMEPAD
   { &kNS_GAMEPAD_TEST_CID, false, nullptr, GamepadServiceTestConstructor },
 #endif
 #ifdef ACCESSIBILITY
   { &kNS_ACCESSIBILITY_SERVICE_CID, false, nullptr, CreateA11yService },
 #endif
   { &kTELEPHONY_SERVICE_CID, false, nullptr, nsITelephonyServiceConstructor },
-  { &kNS_MOBILE_CONNECTION_SERVICE_CID, false, NULL, nsIMobileConnectionServiceConstructor },
+#ifdef MOZ_B2G_RIL
+  { &kNS_MOBILE_CONNECTION_SERVICE_CID, true, NULL, nsIMobileConnectionServiceConstructor },
+#endif
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
   { "@mozilla.org/layout/xul-boxobject-listbox;1", &kNS_LISTBOXOBJECT_CID },
@@ -1231,17 +1273,19 @@ static const mozilla::Module::ContractID
 #endif
   { MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
 #ifdef ACCESSIBILITY
   { "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID },
   { "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
 #endif
   { TELEPHONY_SERVICE_CONTRACTID, &kTELEPHONY_SERVICE_CID },
   { "@mozilla.org/gecko-media-plugin-service;1",  &kGECKO_MEDIA_PLUGIN_SERVICE_CID },
+#ifdef MOZ_B2G_RIL
   { NS_MOBILE_CONNECTION_SERVICE_CONTRACTID, &kNS_MOBILE_CONNECTION_SERVICE_CID },
+#endif
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
   XPCONNECT_CATEGORIES
   { "content-policy", NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID, NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID },
   { "content-policy", NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID, NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID },
   { "content-policy", "CSPService", CSPSERVICE_CONTRACTID },
@@ -1254,17 +1298,19 @@ static const mozilla::Module::CategoryEn
 #endif
   CONTENTDLF_CATEGORIES
 #ifdef MOZ_WIDGET_GONK
   { "profile-after-change", "Gonk System Worker Manager", SYSTEMWORKERMANAGER_CONTRACTID },
 #endif
 #ifdef MOZ_B2G_BT
   { "profile-after-change", "Bluetooth Service", BLUETOOTHSERVICE_CONTRACTID },
 #endif
+#ifdef MOZ_B2G_RIL
   { "profile-after-change", "MobileConnection Service", NS_MOBILE_CONNECTION_SERVICE_CONTRACTID },
+#endif
   { nullptr }
 };
 
 static void
 LayoutModuleDtor()
 {
   Shutdown();
   nsContentUtils::XPCOMShutdown();
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2722,42 +2722,16 @@ ClipListsExceptCaret(nsDisplayListCollec
   ::ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aClip);
   ::ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aClip);
   ::ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aClip);
   ::ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aClip);
   ::ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aClip);
   ::ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aClip);
 }
 
-static bool
-DisplayportExceedsMaxTextureSize(nsPresContext* aPresContext, const nsRect& aDisplayPort, bool aLowPrecision)
-{
-#ifdef MOZ_WIDGET_GONK
-  // On B2G we actively run into max texture size limits because the displayport-sizing code
-  // in the AsyncPanZoomController code is slightly busted (bug 957668 will fix it properly).
-  // We sometimes end up requesting displayports for which the corresponding layer will be
-  // larger than the max GL texture size (which we assume to be 4096 here).
-  // If we run into this case, we should just not use the displayport at all and fall back to
-  // just making a ScrollInfoLayer so that we use the APZC's synchronous scrolling fallback
-  // mechanism.
-  // Note also that if we don't do this here, it is quite likely that the parent B2G process
-  // will kill this child process to prevent OOMs (see the patch that landed as part of bug
-  // 965945 for details).
-  gfxSize resolution = aPresContext->PresShell()->GetCumulativeResolution();
-  if (aLowPrecision) {
-    resolution.width *= gfxPrefs::LowPrecisionResolution();
-    resolution.height *= gfxPrefs::LowPrecisionResolution();
-  }
-  return (aPresContext->AppUnitsToDevPixels(aDisplayPort.width) * resolution.width > 4096) ||
-         (aPresContext->AppUnitsToDevPixels(aDisplayPort.height) * resolution.height > 4096);
-#else
-  return false;
-#endif
-}
-
 void
 ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   if (aBuilder->IsForImageVisibility()) {
     mLastUpdateImagesPos = GetScrollPosition();
   }
@@ -2847,26 +2821,20 @@ ScrollFrameHelper::BuildDisplayList(nsDi
           *aBuilder, mOuter, displayportBase, &displayPort);
     } else {
       // For a root frmae, just get the value of the existing of the display
       // port, if any.
       usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
     }
 
     bool usingLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
-    if (usingDisplayport && DisplayportExceedsMaxTextureSize(mOuter->PresContext(), displayPort, usingLowPrecision)) {
-      usingDisplayport = false;
-    }
     if (usingDisplayport && usingLowPrecision) {
       // If we have low-res painting enabled we should check the critical displayport too
       nsRect critDp;
-      bool usingCritDp = nsLayoutUtils::GetCriticalDisplayPort(mOuter->GetContent(), &critDp);
-      if (usingCritDp && !critDp.IsEmpty() && DisplayportExceedsMaxTextureSize(mOuter->PresContext(), critDp, false)) {
-        usingDisplayport = false;
-      }
+      nsLayoutUtils::GetCriticalDisplayPort(mOuter->GetContent(), &critDp);
     }
 
     // Override the dirty rectangle if the displayport has been set.
     if (usingDisplayport) {
       dirtyRect = displayPort;
     }
   }
 
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -144,16 +144,21 @@
 
             <meta-data android:name="com.sec.minimode.icon.landscape.normal"
                        android:resource="@drawable/icon" />
 
             <intent-filter>
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CALLBACK" />
             </intent-filter>
 
+            <intent-filter>
+                <action android:name="org.mozilla.gecko.GUEST_SESSION_INPROGRESS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
             <!-- Notification API V2 -->
             <intent-filter>
                 <action android:name="@ANDROID_PACKAGE_NAME@.helperBroadcastAction" />
                 <data android:scheme="moz-notification" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
 
             <intent-filter>
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -180,17 +180,17 @@ public class BrowserApp extends GeckoApp
         public boolean checked;
         public boolean enabled = true;
         public boolean visible = true;
         public int parent;
         public boolean added;   // So we can re-add after a locale change.
     }
 
     // The types of guest mdoe dialogs we show
-    private static enum GuestModeDialog {
+    public static enum GuestModeDialog {
         ENTERING,
         LEAVING
     }
 
     private Vector<MenuItemInfo> mAddonMenuItemsCache;
     private PropertyAnimator mMainLayoutAnimator;
 
     private static final Interpolator sTabsInterpolator = new Interpolator() {
@@ -531,21 +531,25 @@ public class BrowserApp extends GeckoApp
 
         mBrowserChrome = (ViewGroup) findViewById(R.id.browser_chrome);
         mActionBarFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar);
         mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
 
         mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
         mProgressView = (ToolbarProgressView) findViewById(R.id.progress);
         mBrowserToolbar.setProgressBar(mProgressView);
-        if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+
+        final String action = intent.getAction();
+        if (Intent.ACTION_VIEW.equals(action)) {
             // Show the target URL immediately in the toolbar.
             mBrowserToolbar.setTitle(intent.getDataString());
 
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
+        } else if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
+            GuestSession.handleIntent(this, intent);
         }
 
         if (NewTabletUI.isEnabled(this)) {
             findViewById(R.id.new_tablet_tab_strip).setVisibility(View.VISIBLE);
         }
 
         ((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideOnTouchListener());
         ((GeckoApp.MainLayout) mMainLayout).setMotionEventInterceptor(new MotionEventInterceptor() {
@@ -655,16 +659,23 @@ public class BrowserApp extends GeckoApp
         if (mediaManagerClass != null) {
             try {
                 Method init = mediaManagerClass.getMethod("init", Context.class);
                 init.invoke(null, this);
             } catch(Exception ex) {
                 Log.e(LOGTAG, "Error initializing media manager", ex);
             }
         }
+
+        if (getProfile().inGuestMode()) {
+            GuestSession.showNotification(this);
+        } else {
+            // If we're restarting, we won't destroy the activity. Make sure we remove any guest notifications that might have been shown.
+            GuestSession.hideNotification(this);
+        }
     }
 
     private void registerOnboardingReceiver(Context context) {
         final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
 
         // Receiver for launching first run start pane on new profile creation.
         mOnboardingReceiver = new BroadcastReceiver() {
             @Override
@@ -1028,16 +1039,18 @@ public class BrowserApp extends GeckoApp
             mOrderedBroadcastHelper = null;
         }
 
         if (mBrowserHealthReporter != null) {
             mBrowserHealthReporter.uninit();
             mBrowserHealthReporter = null;
         }
 
+        GuestSession.onDestroy(this);
+
         EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
             "Menu:Update",
             "Reader:Added",
             "Reader:FaviconRequest",
             "Search:Keyword",
             "Prompt:ShowTop",
             "Accounts:Exist");
 
@@ -1808,21 +1821,21 @@ public class BrowserApp extends GeckoApp
      * tabs loaded by the time the user enters editing mode e.g. just after
      * the app starts. In this case, we simply fallback to an empty URL.
      */
     private void enterEditingMode() {
         String url = "";
 
         final Tab tab = Tabs.getInstance().getSelectedTab();
         if (tab != null) {
-            final String userSearch = tab.getUserSearch();
+            final String userRequested = tab.getUserRequested();
 
             // Check to see if there's a user-entered search term,
             // which we save whenever the user performs a search.
-            url = (TextUtils.isEmpty(userSearch) ? tab.getURL() : userSearch);
+            url = (TextUtils.isEmpty(userRequested) ? tab.getURL() : userRequested);
         }
 
         enterEditingMode(url);
     }
 
     /**
      * Enters editing mode with the specified URL. This method will
      * always open the HISTORY page on about:home.
@@ -2826,17 +2839,17 @@ public class BrowserApp extends GeckoApp
         // a chance.
         if (onContextItemSelected(item)) {
             return true;
         }
 
         return super.onOptionsItemSelected(item);
     }
 
-    private void showGuestModeDialog(final GuestModeDialog type) {
+    public void showGuestModeDialog(final GuestModeDialog type) {
         final Prompt ps = new Prompt(this, new Prompt.PromptCallback() {
             @Override
             public void onPromptFinished(String result) {
                 try {
                     int itemId = new JSONObject(result).getInt("button");
                     if (itemId == 0) {
                         String args = "";
                         if (type == GuestModeDialog.ENTERING) {
@@ -2918,16 +2931,18 @@ public class BrowserApp extends GeckoApp
 
         if (!mInitialized) {
             return;
         }
 
         // Only solicit feedback when the app has been launched from the icon shortcut.
         if (!Intent.ACTION_MAIN.equals(action)) {
             return;
+        } else if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
+            GuestSession.handleIntent(this, intent);
         }
 
         // Check to see how many times the app has been launched.
         final String keyName = getPackageName() + ".feedback_launch_count";
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         // Faster on main thread with an async apply().
         try {
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1428,22 +1428,16 @@ public abstract class GeckoApp
         } else {
             startupAction = StartupAction.NORMAL;
         }
 
         // Start migrating as early as possible, can do this in
         // parallel with Gecko load.
         checkMigrateProfile();
 
-        Uri data = intent.getData();
-        if (data != null && "http".equals(data.getScheme())) {
-            startupAction = StartupAction.PREFETCH;
-            ThreadUtils.postToBackgroundThread(new PrefetchRunnable(data.toString()));
-        }
-
         Tabs.registerOnTabsChangedListener(this);
 
         initializeChrome();
 
         // If we are doing a restore, read the session data and send it to Gecko
         if (!mIsRestoringActivity) {
             String restoreMessage = null;
             if (mShouldRestore) {
@@ -1772,53 +1766,16 @@ public abstract class GeckoApp
         }
     }
 
     public String getDefaultUAString() {
         return HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET :
                                           AppConstants.USER_AGENT_FENNEC_MOBILE;
     }
 
-    public String getUAStringForHost(String host) {
-        // With our standard UA String, we get a 200 response code and
-        // client-side redirect from t.co. This bot-like UA gives us a
-        // 301 response code
-        if ("t.co".equals(host)) {
-            return AppConstants.USER_AGENT_BOT_LIKE;
-        }
-        return getDefaultUAString();
-    }
-
-    class PrefetchRunnable implements Runnable {
-        private String mPrefetchUrl;
-
-        PrefetchRunnable(String prefetchUrl) {
-            mPrefetchUrl = prefetchUrl;
-        }
-
-        @Override
-        public void run() {
-            HttpURLConnection connection = null;
-            try {
-                URL url = new URL(mPrefetchUrl);
-                // data url should have an http scheme
-                connection = (HttpURLConnection) url.openConnection();
-                connection.setRequestProperty("User-Agent", getUAStringForHost(url.getHost()));
-                connection.setInstanceFollowRedirects(false);
-                connection.setRequestMethod("GET");
-                connection.connect();
-            } catch (Exception e) {
-                Log.e(LOGTAG, "Exception prefetching URL", e);
-            } finally {
-                if (connection != null)
-                    connection.disconnect();
-            }
-        }
-    }
-
     private void processAlertCallback(Intent intent) {
         String alertName = "";
         String alertCookie = "";
         Uri data = intent.getData();
         if (data != null) {
             alertName = data.getQueryParameter("name");
             if (alertName == null)
                 alertName = "";
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -326,17 +326,17 @@ public final class GeckoProfile {
         return mLocked == LockState.LOCKED;
     }
 
     public boolean lock() {
         try {
             // If this dir doesn't exist getDir will create it for us
             final File lockFile = new File(getDir(), LOCK_FILE_NAME);
             final boolean result = lockFile.createNewFile();
-            if (result) {
+            if (lockFile.exists()) {
                 mLocked = LockState.LOCKED;
             } else {
                 mLocked = LockState.UNLOCKED;
             }
             return result;
         } catch(IOException ex) {
             Log.e(LOGTAG, "Error locking profile", ex);
         }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GuestSession.java
@@ -0,0 +1,50 @@
+package org.mozilla.gecko;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+// Utility methods for entering/exiting guest mode.
+public class GuestSession {
+    public static final String NOTIFICATION_INTENT = "org.mozilla.gecko.GUEST_SESSION_INPROGRESS";
+
+    private static PendingIntent getNotificationIntent(Context context) {
+        Intent intent = new Intent(NOTIFICATION_INTENT);
+        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    public static void showNotification(Context context) {
+        final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+        final Resources res = context.getResources();
+        builder.setContentTitle(res.getString(R.string.guest_browsing_notification_title))
+               .setContentText(res.getString(R.string.guest_browsing_notification_text))
+               .setSmallIcon(R.drawable.alert_guest)
+               .setOngoing(true)
+               .setContentIntent(getNotificationIntent(context));
+
+        final NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        manager.notify(R.id.guestNotification, builder.build());
+    }
+
+    public static void hideNotification(Context context) {
+        final NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        manager.cancel(R.id.guestNotification);
+    }
+
+    public static void onDestroy(Context context) {
+        if (GeckoProfile.get(context).inGuestMode()) {
+            hideNotification(context);
+        }
+    }
+
+    public static void handleIntent(BrowserApp context, Intent intent) {
+        context.showGuestModeDialog(BrowserApp.GuestModeDialog.LEAVING);
+    }
+
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/RemoteClientsDialogFragment.java
@@ -0,0 +1,125 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mozilla.gecko.TabsAccessor.RemoteClient;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.util.SparseBooleanArray;
+
+/**
+ * A dialog fragment that displays a list of remote clients.
+ * <p>
+ * The dialog allows both single (one tap) and multiple (checkbox) selection.
+ * The dialog's results are communicated via the {@link RemoteClientsListener}
+ * interface. Either the dialog fragment's <i>target fragment</i> (see
+ * {@link Fragment#setTargetFragment(Fragment, int)}), or the containing
+ * <i>activity</i>, must implement that interface. See
+ * {@link #notifyListener(List)} for details.
+ */
+public class RemoteClientsDialogFragment extends DialogFragment {
+    private static final String KEY_TITLE = "title";
+    private static final String KEY_CHOICE_MODE = "choice_mode";
+    private static final String KEY_POSITIVE_BUTTON_TEXT = "positive_button_text";
+    private static final String KEY_CLIENTS = "clients";
+
+    public interface RemoteClientsListener {
+        // Always called on the main UI thread.
+        public void onClients(List<RemoteClient> clients);
+    }
+
+    public enum ChoiceMode {
+        SINGLE,
+        MULTIPLE,
+    }
+
+    public static RemoteClientsDialogFragment newInstance(String title, String positiveButtonText, ChoiceMode choiceMode, ArrayList<RemoteClient> clients) {
+        final RemoteClientsDialogFragment dialog = new RemoteClientsDialogFragment();
+        final Bundle args = new Bundle();
+        args.putString(KEY_TITLE, title);
+        args.putString(KEY_POSITIVE_BUTTON_TEXT, positiveButtonText);
+        args.putInt(KEY_CHOICE_MODE, choiceMode.ordinal());
+        args.putParcelableArrayList(KEY_CLIENTS, clients);
+        dialog.setArguments(args);
+        return dialog;
+    }
+
+    public RemoteClientsDialogFragment() {
+        // Empty constructor is required for DialogFragment.
+    }
+
+    protected void notifyListener(List<RemoteClient> clients) {
+        RemoteClientsListener listener;
+        try {
+            listener = (RemoteClientsListener) getTargetFragment();
+        } catch (ClassCastException e) {
+            try {
+                listener = (RemoteClientsListener) getActivity();
+            } catch (ClassCastException f) {
+                throw new ClassCastException(getTargetFragment() + " or " + getActivity()
+                        + " must implement RemoteClientsListener");
+            }
+        }
+        listener.onClients(clients);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final String title = getArguments().getString(KEY_TITLE);
+        final String positiveButtonText = getArguments().getString(KEY_POSITIVE_BUTTON_TEXT);
+        final ChoiceMode choiceMode = ChoiceMode.values()[getArguments().getInt(KEY_CHOICE_MODE)];
+        final ArrayList<RemoteClient> clients = getArguments().getParcelableArrayList(KEY_CLIENTS);
+
+        final Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(title);
+
+        final String[] clientNames = new String[clients.size()];
+        for (int i = 0; i < clients.size(); i++) {
+            clientNames[i] = clients.get(i).name;
+        }
+
+        if (choiceMode == ChoiceMode.MULTIPLE) {
+            builder.setMultiChoiceItems(clientNames, null, null);
+            builder.setPositiveButton(positiveButtonText, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialogInterface, int which) {
+                    if (which != Dialog.BUTTON_POSITIVE) {
+                        return;
+                    }
+
+                    final AlertDialog dialog = (AlertDialog) dialogInterface;
+                    final SparseBooleanArray checkedItemPositions = dialog.getListView().getCheckedItemPositions();
+                    final ArrayList<RemoteClient> checked = new ArrayList<RemoteClient>();
+                    for (int i = 0; i < checkedItemPositions.size(); i++) {
+                        if (checkedItemPositions.get(i)) {
+                            checked.add(clients.get(i));
+                        }
+                    }
+                    notifyListener(checked);
+                }
+            });
+        } else {
+            builder.setItems(clientNames, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int index) {
+                    final ArrayList<RemoteClient> checked = new ArrayList<RemoteClient>();
+                    checked.add(clients.get(index));
+                    notifyListener(checked);
+                }
+            });
+        }
+
+        return builder.create();
+    }
+}
--- a/mobile/android/base/RemoteTabsExpandableListAdapter.java
+++ b/mobile/android/base/RemoteTabsExpandableListAdapter.java
@@ -103,28 +103,39 @@ public class RemoteTabsExpandableListAda
 
         final TextView nameView = (TextView) view.findViewById(R.id.client);
         nameView.setText(client.name);
 
         final TextView lastModifiedView = (TextView) view.findViewById(R.id.last_synced);
         final long now = System.currentTimeMillis();
         lastModifiedView.setText(TabsAccessor.getLastSyncedString(context, now, client.lastModified));
 
-        // This view exists only in some of our group views: it's present
+        // These views exists only in some of our group views: they are present
         // for the home panel groups and not for the tabs tray groups.
         // Therefore, we must handle null.
         final ImageView deviceTypeView = (ImageView) view.findViewById(R.id.device_type);
         if (deviceTypeView != null) {
             if ("desktop".equals(client.deviceType)) {
                 deviceTypeView.setBackgroundResource(R.drawable.sync_desktop);
             } else {
                 deviceTypeView.setBackgroundResource(R.drawable.sync_mobile);
             }
         }
 
+        final ImageView deviceExpandedView = (ImageView) view.findViewById(R.id.device_expanded);
+        if (deviceExpandedView != null) {
+            // If there are no tabs to display, don't show an indicator at all.
+            if (client.tabs.isEmpty()) {
+                deviceExpandedView.setBackgroundResource(0);
+            } else {
+                final int resourceId = isExpanded ? R.drawable.home_group_expanded : R.drawable.home_group_collapsed;
+                deviceExpandedView.setBackgroundResource(resourceId);
+            }
+        }
+
         return view;
     }
 
     @Override
     public boolean isChildSelectable(int groupPosition, int childPosition) {
         return true;
     }
 
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -33,17 +33,17 @@ import android.view.View;
 public class Tab {
     private static final String LOGTAG = "GeckoTab";
 
     private static Pattern sColorPattern;
     private final int mId;
     private long mLastUsed;
     private String mUrl;
     private String mBaseDomain;
-    private String mUserSearch;
+    private String mUserRequested; // The original url requested. May be typed by the user or sent by an extneral app for example.
     private String mTitle;
     private Bitmap mFavicon;
     private String mFaviconUrl;
     private int mFaviconSize;
     private boolean mHasFeeds;
     private boolean mHasOpenSearch;
     private SiteIdentity mSiteIdentity;
     private boolean mReaderEnabled;
@@ -92,17 +92,17 @@ public class Tab {
         NONE         // Non error pages
     }
 
     public Tab(Context context, int id, String url, boolean external, int parentId, String title) {
         mAppContext = context.getApplicationContext();
         mId = id;
         mUrl = url;
         mBaseDomain = "";
-        mUserSearch = "";
+        mUserRequested = "";
         mExternal = external;
         mParentId = parentId;
         mTitle = title == null ? "" : title;
         mSiteIdentity = new SiteIdentity();
         mHistoryIndex = -1;
         mContentType = "";
         mZoomConstraints = new ZoomConstraints(false);
         mPluginViews = new ArrayList<View>();
@@ -142,19 +142,19 @@ public class Tab {
         return mParentId;
     }
 
     // may be null if user-entered query hasn't yet been resolved to a URI
     public synchronized String getURL() {
         return mUrl;
     }
 
-    // mUserSearch should never be null, but it may be an empty string
-    public synchronized String getUserSearch() {
-        return mUserSearch;
+    // mUserRequested should never be null, but it may be an empty string
+    public synchronized String getUserRequested() {
+        return mUserRequested;
     }
 
     // mTitle should never be null, but it may be an empty string
     public synchronized String getTitle() {
         return mTitle;
     }
 
     public String getDisplayTitle() {
@@ -263,18 +263,18 @@ public class Tab {
     }
 
     public synchronized void updateURL(String url) {
         if (url != null && url.length() > 0) {
             mUrl = url;
         }
     }
 
-    private synchronized void updateUserSearch(String userSearch) {
-        mUserSearch = userSearch;
+    public synchronized void updateUserRequested(String userRequested) {
+        mUserRequested = userRequested;
     }
 
     public void setErrorType(String type) {
         if ("blocked".equals(type))
             setErrorType(ErrorType.BLOCKED);
         else if ("certerror".equals(type))
             setErrorType(ErrorType.CERT_ERROR);
         else if ("neterror".equals(type))
@@ -649,17 +649,17 @@ public class Tab {
         if (sameDocument) {
             // We can get a location change event for the same document with an anchor tag
             // Notify listeners so that buttons like back or forward will update themselves
             Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl);
             return;
         }
 
         setContentType(message.getString("contentType"));
-        updateUserSearch(message.getString("userSearch"));
+        updateUserRequested(message.getString("userRequested"));
         mBaseDomain = message.optString("baseDomain");
 
         setHasFeeds(false);
         setHasOpenSearch(false);
         updateIdentityData(null);
         setReaderEnabled(false);
         setZoomConstraints(new ZoomConstraints(true));
         setHasTouchListeners(false);
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -483,16 +483,17 @@ public class Tabs implements GeckoEventL
                         notifyListeners(tab, Tabs.TabEvents.STOP);
                     }
                 }
             } else if (event.equals("Content:LoadError")) {
                 tab.handleContentLoaded();
                 notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
             } else if (event.equals("Content:PageShow")) {
                 notifyListeners(tab, TabEvents.PAGE_SHOW);
+                tab.updateUserRequested(message.getString("userRequested"));
             } else if (event.equals("DOMContentLoaded")) {
                 tab.handleContentLoaded();
                 String backgroundColor = message.getString("bgColor");
                 if (backgroundColor != null) {
                     tab.setBackgroundColor(backgroundColor);
                 } else {
                     // Default to white if no color is given
                     tab.setBackgroundColor(Color.WHITE);
--- a/mobile/android/base/TabsAccessor.java
+++ b/mobile/android/base/TabsAccessor.java
@@ -15,16 +15,18 @@ import org.mozilla.gecko.db.BrowserContr
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UIAsyncTask;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
 
 public final class TabsAccessor {
     private static final String LOGTAG = "GeckoTabsAccessor";
 
     public static final String[] TABS_PROJECTION_COLUMNS = new String[] {
@@ -53,49 +55,109 @@ public final class TabsAccessor {
     private static final Pattern FILTERED_URL_PATTERN = Pattern.compile("^(about|chrome|wyciwyg|file):");
 
     /**
      * A thin representation of a remote client.
      * <p>
      * We use the hash of the client's GUID as the ID in
      * {@link RemoteTabsExpandableListAdapter#getGroupId(int)}.
      */
-    public static class RemoteClient {
+    public static class RemoteClient implements Parcelable {
         public final String guid;
         public final String name;
         public final long lastModified;
         public final String deviceType;
         public final ArrayList<RemoteTab> tabs;
 
         public RemoteClient(String guid, String name, long lastModified, String deviceType) {
             this.guid = guid;
             this.name = name;
             this.lastModified = lastModified;
             this.deviceType = deviceType;
             this.tabs = new ArrayList<RemoteTab>();
         }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(guid);
+            parcel.writeString(name);
+            parcel.writeLong(lastModified);
+            parcel.writeString(deviceType);
+            parcel.writeTypedList(tabs);
+        }
+
+        public static final Creator<RemoteClient> CREATOR = new Creator<RemoteClient>() {
+            @Override
+            public RemoteClient createFromParcel(final Parcel source) {
+                final String guid = source.readString();
+                final String name = source.readString();
+                final long lastModified = source.readLong();
+                final String deviceType = source.readString();
+
+                final RemoteClient client = new RemoteClient(guid, name, lastModified, deviceType);
+                source.readTypedList(client.tabs, RemoteTab.CREATOR);
+
+                return client;
+            }
+
+            @Override
+            public RemoteClient[] newArray(final int size) {
+                return new RemoteClient[size];
+            }
+        };
     }
 
     /**
      * A thin representation of a remote tab.
      * <p>
      * We use the hash of the tab as the ID in
      * {@link RemoteTabsExpandableListAdapter#getClientId(int)}, and therefore we
      * must implement equality as well. These are generated functions.
      */
-    public static class RemoteTab {
+    public static class RemoteTab implements Parcelable {
         public final String title;
         public final String url;
 
         public RemoteTab(String title, String url) {
             this.title = title;
             this.url = url;
         }
 
         @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(title);
+            parcel.writeString(url);
+        }
+
+        public static final Creator<RemoteTab> CREATOR = new Creator<RemoteTab>() {
+            @Override
+            public RemoteTab createFromParcel(final Parcel source) {
+                final String title = source.readString();
+                final String url = source.readString();
+
+                return new RemoteTab(title, url);
+            }
+
+            @Override
+            public RemoteTab[] newArray(final int size) {
+                return new RemoteTab[size];
+            }
+        };
+
+        @Override
         public int hashCode() {
             final int prime = 31;
             int result = 1;
             result = prime * result + ((title == null) ? 0 : title.hashCode());
             result = prime * result + ((url == null) ? 0 : url.hashCode());
             return result;
         }
 
--- a/mobile/android/base/favicons/cache/FaviconCache.java
+++ b/mobile/android/base/favicons/cache/FaviconCache.java
@@ -95,18 +95,18 @@ public class FaviconCache {
     private static final String LOGTAG = "FaviconCache";
 
     // The number of spaces to allocate for favicons in each node.
     private static final int NUM_FAVICON_SIZES = 4;
 
     // Dimensions of the largest favicon to store in the cache. Everything is downscaled to this.
     public final int maxCachedWidth;
 
-    // Retry failed favicons after 20 minutes.
-    public static final long FAILURE_RETRY_MILLISECONDS = 1000 * 60 * 20;
+    // Retry failed favicons after four hours.
+    public static final long FAILURE_RETRY_MILLISECONDS = 1000 * 60 * 60 * 4;
 
     // Map relating Favicon URLs with objects representing decoded favicons.
     // Since favicons may be container formats holding multiple icons, the underlying type holds a
     // sorted list of bitmap payloads in ascending order of size. The underlying type may be queried
     // for the least larger payload currently present.
     private final HashMap<String, FaviconsForURL> backingMap = new HashMap<String, FaviconsForURL>();
 
     // And the same, but never evicted.
--- a/mobile/android/base/home/RemoteTabsExpandableListFragment.java
+++ b/mobile/android/base/home/RemoteTabsExpandableListFragment.java
@@ -1,19 +1,25 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.home;
 
+import java.util.ArrayList;
 import java.util.EnumSet;
+import java.util.Iterator;
 import java.util.List;
 
+import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.RemoteClientsDialogFragment;
+import org.mozilla.gecko.RemoteClientsDialogFragment.ChoiceMode;
+import org.mozilla.gecko.RemoteClientsDialogFragment.RemoteClientsListener;
 import org.mozilla.gecko.RemoteTabsExpandableListAdapter;
 import org.mozilla.gecko.TabsAccessor;
 import org.mozilla.gecko.TabsAccessor.RemoteClient;
 import org.mozilla.gecko.TabsAccessor.RemoteTab;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
@@ -22,50 +28,69 @@ import org.mozilla.gecko.widget.GeckoSwi
 
 import android.accounts.Account;
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
 import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.ExpandableListAdapter;
 import android.widget.ExpandableListView;
 import android.widget.ExpandableListView.OnChildClickListener;
 import android.widget.ExpandableListView.OnGroupClickListener;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 /**
  * Fragment that displays tabs from other devices in an <code>ExpandableListView<code>.
  * <p>
  * This is intended to be used on phones, and possibly in portrait mode on tablets.
  */
-public class RemoteTabsExpandableListFragment extends HomeFragment {
+public class RemoteTabsExpandableListFragment extends HomeFragment implements RemoteClientsListener {
     // Logging tag name.
     private static final String LOGTAG = "GeckoRemoteTabsExpList";
 
     // Cursor loader ID.
     private static final int LOADER_ID_REMOTE_TABS = 0;
 
+    // Dialog fragment TAG.
+    private static final String DIALOG_TAG_REMOTE_TABS = "dialog_tag_remote_tabs";
+
     private static final String[] STAGES_TO_SYNC_ON_REFRESH = new String[] { "clients", "tabs" };
 
+    // Maintain group collapsed and hidden state.
+    // Only accessed from the UI thread.
+    private static RemoteTabsExpandableListState sState;
+
     // Adapter for the list of remote tabs.
     private RemoteTabsExpandableListAdapter mAdapter;
 
+    // List of hidden remote clients.
+    // Only accessed from the UI thread.
+    private final ArrayList<RemoteClient> mHiddenClients = new ArrayList<RemoteClient>();
+
     // The view shown by the fragment.
     private HomeExpandableListView mList;
 
     // Reference to the View to display when there are no results.
     private View mEmptyView;
 
+    // The footer view to display when there are hidden devices not shown.
+    private View mFooterView;
+
     // Callbacks used for the loader.
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
 
     // Child refresh layout view.
     private GeckoSwipeRefreshLayout mRefreshLayout;
 
     // Sync listener that stops refreshing when a sync is completed.
     private RemoteTabsSyncListener mSyncStatusListener;
@@ -112,42 +137,48 @@ public class RemoteTabsExpandableListFra
                 mUrlOpenListener.onUrlOpen(tab.url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
                 return true;
             }
         });
 
         mList.setOnGroupClickListener(new OnGroupClickListener() {
             @Override
             public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
-                // Since we don't indicate the expansion state yet, don't allow
-                // collapsing groups at all.
-                return true;
+                final ExpandableListAdapter adapter = parent.getExpandableListAdapter();
+                final RemoteClient client = (RemoteClient) adapter.getGroup(groupPosition);
+                if (client != null) {
+                    // After we process this click, the group's expanded state will have flipped.
+                    sState.setClientCollapsed(client.guid, mList.isGroupExpanded(groupPosition));
+                }
+
+                // We want the system to handle the click, expanding or collapsing as necessary.
+                return false;
             }
         });
 
         // Show a context menu only for tabs (not for clients).
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.ExpandableFactory() {
             @Override
             public HomeContextMenuInfo makeInfoForAdapter(View view, int position, long id, ExpandableListAdapter adapter) {
                 long packedPosition = mList.getExpandableListPosition(position);
-                if (ExpandableListView.getPackedPositionType(packedPosition) != ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
-                    return null;
-                }
                 final int groupPosition = ExpandableListView.getPackedPositionGroup(packedPosition);
-                final int childPosition = ExpandableListView.getPackedPositionChild(packedPosition);
-                final Object child = adapter.getChild(groupPosition, childPosition);
-                if (child instanceof RemoteTab) {
-                    final RemoteTab tab = (RemoteTab) child;
+                final int type = ExpandableListView.getPackedPositionType(packedPosition);
+                if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
+                    final int childPosition = ExpandableListView.getPackedPositionChild(packedPosition);
+                    final RemoteTab tab = (RemoteTab) adapter.getChild(groupPosition, childPosition);
                     final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
                     info.url = tab.url;
                     info.title = tab.title;
                     return info;
-                } else {
-                    return null;
+                } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+                    final RemoteClient client = (RemoteClient) adapter.getGroup(groupPosition);
+                    final RemoteTabsClientContextMenuInfo info = new RemoteTabsClientContextMenuInfo(view, position, id, client);
+                    return info;
                 }
+                return null;
             }
         });
 
         registerForContextMenu(mList);
     }
 
     @Override
     public void onDestroyView() {
@@ -160,49 +191,190 @@ public class RemoteTabsExpandableListFra
             mSyncStatusListener = null;
         }
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
+        // This races when multiple Fragments are created. That's okay: one
+        // will win, and thereafter, all will be okay. If we create and then
+        // drop an instance the shared SharedPreferences backing all the
+        // instances will maintain the state for us. Since everything happens on
+        // the UI thread, this doesn't even need to be volatile.
+        if (sState == null) {
+            sState = new RemoteTabsExpandableListState(GeckoSharedPrefs.forProfile(getActivity()));
+        }
+
+        // There is an unfortunate interaction between ExpandableListViews and
+        // footer onClick handling. The footer view itself appears to not
+        // receive click events. Its children, however, do receive click events.
+        // Therefore, we attach an onClick handler to a child of the footer view
+        // itself.
+        mFooterView = LayoutInflater.from(getActivity()).inflate(R.layout.home_remote_tabs_hidden_devices_footer, mList, false);
+        final View view = mFooterView.findViewById(R.id.hidden_devices);
+        view.setClickable(true);
+        view.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final RemoteClientsDialogFragment dialog = RemoteClientsDialogFragment.newInstance(
+                        getResources().getString(R.string.home_remote_tabs_hidden_devices_title),
+                        getResources().getString(R.string.home_remote_tabs_unhide_selected_devices),
+                        ChoiceMode.MULTIPLE, new ArrayList<RemoteClient>(mHiddenClients));
+                dialog.setTargetFragment(RemoteTabsExpandableListFragment.this, 0);
+                dialog.show(getActivity().getSupportFragmentManager(), DIALOG_TAG_REMOTE_TABS);
+            }
+        });
+
+        // There is a delicate interaction, pre-KitKat, between
+        // {add,remove}FooterView and setAdapter. setAdapter wraps the adapter
+        // in a footer/header-managing adapter, which only happens (pre-KitKat)
+        // if a footer/header is present. Therefore, we add our footer before
+        // setting the adapter; and then we remove it afterward. From there on,
+        // we can add/remove it at will.
+        mList.addFooterView(mFooterView, null, true);
+
         // Intialize adapter
         mAdapter = new RemoteTabsExpandableListAdapter(R.layout.home_remote_tabs_group, R.layout.home_remote_tabs_child, null);
         mList.setAdapter(mAdapter);
 
+        // Now the adapter is wrapped; we can remove our footer view.
+        mList.removeFooterView(mFooterView);
+
         // Create callbacks before the initial loader is started
         mCursorLoaderCallbacks = new CursorLoaderCallbacks();
         loadIfVisible();
     }
 
-    private void updateUiFromClients(List<RemoteClient> clients) {
-        if (clients != null && !clients.isEmpty()) {
-            for (int i = 0; i < mList.getExpandableListAdapter().getGroupCount(); i++) {
-                mList.expandGroup(i);
-            }
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        if (!(menuInfo instanceof RemoteTabsClientContextMenuInfo)) {
+            // Long pressed item was not a RemoteTabsGroup item. Superclass
+            // can handle this.
+            super.onCreateContextMenu(menu, view, menuInfo);
             return;
         }
 
-        // Cursor is empty, so set the empty view if it hasn't been set already.
+        // Long pressed item was a remote client; provide the appropriate menu.
+        final MenuInflater inflater = new MenuInflater(view.getContext());
+        inflater.inflate(R.menu.home_remote_tabs_client_contextmenu, menu);
+
+        final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
+        menu.setHeaderTitle(info.client.name);
+
+        // Hide unused menu items.
+        final boolean isHidden = sState.isClientHidden(info.client.guid);
+        final MenuItem item = menu.findItem(isHidden
+                ? R.id.home_remote_tabs_hide_client
+                : R.id.home_remote_tabs_show_client);
+        item.setVisible(false);
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        if (super.onContextItemSelected(item)) {
+            // HomeFragment was able to handle to selected item.
+            return true;
+        }
+
+        final ContextMenuInfo menuInfo = item.getMenuInfo();
+        if (!(menuInfo instanceof RemoteTabsClientContextMenuInfo)) {
+            return false;
+        }
+
+        final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
+
+        final int itemId = item.getItemId();
+        if (itemId == R.id.home_remote_tabs_hide_client) {
+            sState.setClientHidden(info.client.guid, true);
+            getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
+            return true;
+        } else if (itemId == R.id.home_remote_tabs_show_client) {
+            sState.setClientHidden(info.client.guid, false);
+            getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
+            return true;
+        }
+        return false;
+    }
+
+    private void updateUiFromClients(List<RemoteClient> clients, List<RemoteClient> hiddenClients) {
+        // We have three states: no clients (including hidden clients) at all;
+        // all clients hidden; some clients hidden. We want to show the empty
+        // list view only when we have no clients at all. This flag
+        // differentiates the first from the latter two states.
+        boolean displayedSomeClients = false;
+
+        if (hiddenClients == null || hiddenClients.isEmpty()) {
+            mList.removeFooterView(mFooterView);