Merge b2g-inbound to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 08 Sep 2013 08:28:14 -0700
changeset 146096 c7cc85e13f7ab547bcf062985d5ddb2eb09f2e6a
parent 146080 0899b763a06608d2ecab5f2e2b96b437e828de32 (current diff)
parent 146095 789594eff47b38d0cb6e835f61c3f00f2c0900c9 (diff)
child 146097 ac968ae7cb1ef16445658901336a495e2f1ad71c
child 146115 e5ff11d87af5f7c49548ce83324a31402e08889f
child 146122 3fd62a46f15489c1b5d2870b3ba914bf8b6e995c
push id25239
push userphilringnalda@gmail.com
push dateSun, 08 Sep 2013 15:34:22 +0000
treeherdermozilla-central@c7cc85e13f7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
c7cc85e13f7a / 26.0a1 / 20130909030204 / files
nightly linux64
c7cc85e13f7a / 26.0a1 / 20130909030204 / files
nightly mac
c7cc85e13f7a / 26.0a1 / 20130909030204 / files
nightly win32
c7cc85e13f7a / 26.0a1 / 20130909030204 / files
nightly win64
c7cc85e13f7a / 26.0a1 / 20130909030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge b2g-inbound to m-c
b2g/chrome/content/shell.xul
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -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/. */
 
 #filter substitution
 
-pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
+pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.html");
 pref("browser.chromeURL", "chrome://browser/content/");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
--- a/b2g/chrome/content/screen.js
+++ b/b2g/chrome/content/screen.js
@@ -6,17 +6,17 @@
 // 
 
 // We do this on ContentStart because querying the displayDPI fails otherwise.
 window.addEventListener('ContentStart', function() {
   // This is the toplevel <window> element
   let shell = document.getElementById('shell');
 
   // The <browser> element inside it
-  let browser = document.getElementById('homescreen');
+  let browser = document.getElementById('systemapp');
 
   // Figure out the native resolution of the screen
   let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Components.interfaces.nsIDOMWindowUtils);
   let hostDPI = windowUtils.displayDPI;
 
   let DEFAULT_SCREEN = "320x480";
 
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -309,18 +309,25 @@ let AdbController = {
       //
       // By waiting until both values are properly initialized, we avoid
       // turning adb on or off accidentally.
       if (this.DEBUG) {
         this.debug("updateState: Waiting for all vars to be initialized");
       }
       return;
     }
+
+    // Check if we have a remote debugging session going on. If so, we won't
+    // disable adb even if the screen is locked.
+    let isDebugging = Object.keys(DebuggerServer._connections).length > 0;
+    debug("isDebugging=" + isDebugging);
+
     let enableAdb = this.remoteDebuggerEnabled &&
-      !(this.lockEnabled && this.locked);
+      (!(this.lockEnabled && this.locked) || isDebugging);
+
     let useDisableAdbTimer = true;
     try {
       if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) {
         // Marionette is enabled. Marionette requires that adb be on (and also
         // requires that remote debugging be off). The fact that marionette
         // is enabled also implies that we're doing a non-production build, so
         // we want adb enabled all of the time.
         enableAdb = true;
@@ -361,17 +368,17 @@ let AdbController = {
       }
       try {
         libcutils.property_set("persist.sys.usb.config", newConfig);
       } catch(e) {
         dump("Error configuring adb: " + e);
       }
     }
     if (useDisableAdbTimer) {
-      if (enableAdb) {
+      if (enableAdb && !isDebugging) {
         this.startDisableAdbTimer();
       } else {
         this.stopDisableAdbTimer();
       }
     }
   }
 };
 
rename from b2g/chrome/content/shell.xul
rename to b2g/chrome/content/shell.html
--- a/b2g/chrome/content/shell.xul
+++ b/b2g/chrome/content/shell.html
@@ -1,26 +1,41 @@
-<?xml version="1.0"?>
-
+<!DOCTYPE html>
 <!-- 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/.  -->
 
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        id="shell"
-        windowtype="navigator:browser"
+<html xmlns="http://www.w3.org/1999/xhtml "
+      id="shell"
+      windowtype="navigator:browser"
 #ifdef ANDROID
-        sizemode="fullscreen"
+      sizemode="fullscreen"
 #endif
-        style="background: black; overflow: hidden; width:320px; height:480px"
-        onunload="shell.stop();">
+      style="background: black; overflow: hidden; width:100%; height:100%; padding: 0px !important"
+      onunload="shell.stop();">
 
-  <script type="application/javascript" src="chrome://browser/content/settings.js"/>
-  <script type="application/javascript" src="chrome://browser/content/shell.js"/>
+<head>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/settings.js"> </script>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/shell.js"> </script>
 
 #ifndef MOZ_WIDGET_GONK
+
   <!-- this script handles the screen argument for desktop builds -->
-  <script type="application/javascript" src="chrome://browser/content/screen.js"/>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/screen.js"> </script>
   <!-- this script handles the "runapp" argument for desktop builds -->
-  <script type="application/javascript" src="chrome://browser/content/runapp.js"/>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/runapp.js"> </script>
 #endif
-  <!-- The html:iframe containing the UI is created here. -->
-</window>
+</head>
+  <body id="container" style="margin: 0px; width:100%; height:100%;">
+#ifdef MOZ_WIDGET_COCOA
+    <!--
+     If the document is empty at startup, we don't display the window
+     at all on Mac OS...
+    -->
+    <h1 id="placeholder">wtf mac os!</h1>
+#endif
+    <!-- The html:iframe containing the UI is created here. -->
+  </body>
+</html>
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -178,17 +178,17 @@ var shell = {
 
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-interface-state-changed", false);
   },
 
   get contentBrowser() {
     delete this.contentBrowser;
-    return this.contentBrowser = document.getElementById('homescreen');
+    return this.contentBrowser = document.getElementById('systemapp');
   },
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
@@ -261,35 +261,39 @@ var shell = {
     let homeURL = this.homeURL;
     if (!homeURL) {
       let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       alert(msg);
       return;
     }
 
     let manifestURL = this.manifestURL;
-    // <html:iframe id="homescreen"
+    // <html:iframe id="systemapp"
     //              mozbrowser="true" allowfullscreen="true"
-    //              style="overflow: hidden; -moz-box-flex: 1; border: none;"
+    //              style="overflow: hidden; height: 100%; width: 100%; border: none;"
     //              src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
-    let browserFrame =
+    let systemAppFrame =
       document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
-    browserFrame.setAttribute('id', 'homescreen');
-    browserFrame.setAttribute('mozbrowser', 'true');
-    browserFrame.setAttribute('mozapp', manifestURL);
-    browserFrame.setAttribute('allowfullscreen', 'true');
-    browserFrame.setAttribute('style', "overflow: hidden; -moz-box-flex: 1; border: none;");
-    browserFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
-    document.getElementById('shell').appendChild(browserFrame);
+    systemAppFrame.setAttribute('id', 'systemapp');
+    systemAppFrame.setAttribute('mozbrowser', 'true');
+    systemAppFrame.setAttribute('mozapp', manifestURL);
+    systemAppFrame.setAttribute('allowfullscreen', 'true');
+    systemAppFrame.setAttribute('style', "overflow: hidden; height: 100%; width: 100%; border: none;");
+    systemAppFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
+    let container = document.getElementById('container');
+#ifdef MOZ_WIDGET_COCOA
+    container.removeChild(document.getElementById('placeholder'));
+#endif
+    container.appendChild(systemAppFrame);
 
-    browserFrame.contentWindow
-                .QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
-                .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
-                                    .createInstance(Ci.nsISHistory);
+    systemAppFrame.contentWindow
+                  .QueryInterface(Ci.nsIInterfaceRequestor)
+                  .getInterface(Ci.nsIWebNavigation)
+                  .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
+                                      .createInstance(Ci.nsISHistory);
 
     // Capture all key events so we can filter out hardware buttons
     // And send them to Gaia via mozChromeEvents.
     // Ideally, hardware buttons wouldn't generate key events at all, or
     // if they did, they would use keycodes that conform to DOM 3 Events.
     // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
     window.addEventListener('keydown', this, true);
     window.addEventListener('keypress', this, true);
@@ -1016,16 +1020,20 @@ let RemoteDebugger = {
           DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
         }
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/styleeditor.js");
         DebuggerServer.enableWebappsContentActor = true;
       }
       DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
       DebuggerServer.registerModule("devtools/server/actors/device");
+
+      DebuggerServer.onConnectionChange = function(what) {
+        AdbController.updateState();
+      }
     }
 
     let port = Services.prefs.getIntPref('devtools.debugger.remote-port') || 6000;
     try {
       DebuggerServer.openListener(port);
     } catch (e) {
       dump('Unable to start debugger server: ' + e + '\n');
     }
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -7,17 +7,17 @@
 chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
   content/arrow.svg                     (content/arrow.svg)
 * content/dbg-browser-actors.js         (content/dbg-browser-actors.js)
   content/forms.js                      (content/forms.js)
 * content/settings.js                   (content/settings.js)
-* content/shell.xul                     (content/shell.xul)
+* content/shell.html                    (content/shell.html)
 * content/shell.js                      (content/shell.js)
 #ifndef ANDROID
   content/screen.js                     (content/screen.js)
   content/runapp.js                     (content/runapp.js)
 #endif
 * content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "a04ef5d325988b36c3fb088d160c389a1e8682e5", 
+    "revision": "0bfd8f0e9f4a8ee91237545603cc8a216518de18", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -159,17 +159,16 @@
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_activities.xpt
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_audiochannel.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_system.xpt
 #ifdef MOZ_B2G_RIL
-@BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_voicemail.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_cellbroadcast.xpt
 @BINPATH@/components/dom_wappush.xpt
 #endif
 #ifdef MOZ_B2G_BT
@@ -198,16 +197,17 @@
 @BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
+@BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
 @BINPATH@/components/dom_views.xpt
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
@@ -466,16 +466,18 @@
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/NetworkStatsManager.js
 @BINPATH@/components/NetworkStatsManager.manifest
 @BINPATH@/components/NetworkInterfaceListService.manifest
 @BINPATH@/components/NetworkInterfaceListService.js
+@BINPATH@/components/TelephonyProvider.manifest
+@BINPATH@/components/TelephonyProvider.js
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -171,17 +171,16 @@
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_system.xpt
 #ifdef MOZ_B2G_RIL
-@BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_voicemail.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_wappush.xpt
 #endif
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
@@ -207,16 +206,17 @@
 @BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
+@BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_traversal.xpt
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
 #ifdef MOZ_GAMEPAD
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -49,17 +49,17 @@
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_RIL
-#include "Telephony.h"
+#include "mozilla/dom/telephony/Telephony.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 #include "AudioChannelManager.h"
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -163,22 +163,22 @@ DOMInterfaces = {
 
 'BluetoothManager': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
     'headerFile': 'BluetoothManager.h'
 },
 
 'CallEvent': {
     'nativeType': 'mozilla::dom::telephony::CallEvent',
-    'headerFile': 'CallEvent.h',
+    'headerFile': 'mozilla/dom/telephony/CallEvent.h',
 },
 
 'CallsList': {
     'nativeType': 'mozilla::dom::telephony::CallsList',
-    'headerFile': 'CallsList.h',
+    'headerFile': 'mozilla/dom/telephony/CallsList.h',
 },
 
 'CameraControl': {
     'nativeType': 'mozilla::nsDOMCameraControl',
     'headerFile': 'DOMCameraControl.h',
     'binaryNames': {
         "release": "ReleaseHardware"
     }
@@ -1157,27 +1157,27 @@ DOMInterfaces = {
 },
 
 'SVGZoomAndPan' : {
     'concrete': False,
 },
 
 'Telephony' : {
     'nativeType': 'mozilla::dom::telephony::Telephony',
-    'headerFile': 'Telephony.h',
+    'headerFile': 'mozilla/dom/telephony/Telephony.h',
 },
 
 'TelephonyCall' : {
     'nativeType': 'mozilla::dom::telephony::TelephonyCall',
-    'headerFile': 'TelephonyCall.h',
+    'headerFile': 'mozilla/dom/telephony/TelephonyCall.h',
 },
 
 'TelephonyCallGroup' : {
     'nativeType': 'mozilla::dom::telephony::TelephonyCallGroup',
-    'headerFile': 'TelephonyCallGroup.h',
+    'headerFile': 'mozilla/dom/telephony/TelephonyCallGroup.h',
 },
 
 'Text': {
     # Total hack to allow binding code to realize that nsTextNode can
     # in fact be cast to Text.
     'headerFile': 'nsTextNode.h',
 },
 
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -1478,17 +1478,17 @@ BluetoothHfpManager::OnSocketConnectSucc
     MOZ_ASSERT(!mSocket);
     mHeadsetSocket.swap(mSocket);
 
     mHandsfreeSocket->Disconnect();
     mHandsfreeSocket = nullptr;
   }
 
   nsCOMPtr<nsITelephonyProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(provider);
   provider->EnumerateCalls(mListener->GetListener());
 
   mFirstCKPD = true;
 
   // Cache device path for NotifySettings() since we can't get socket address
   // when a headset disconnect with us
   mSocket->GetAddress(mDeviceAddress);
--- a/dom/bluetooth/BluetoothTelephonyListener.cpp
+++ b/dom/bluetooth/BluetoothTelephonyListener.cpp
@@ -56,23 +56,21 @@ TelephonyListener::EnumerateCallStateCom
 
 NS_IMETHODIMP
 TelephonyListener::EnumerateCallState(uint32_t aCallIndex,
                                       uint16_t aCallState,
                                       const nsAString_internal& aNumber,
                                       bool aIsActive,
                                       bool aIsOutgoing,
                                       bool aIsEmergency,
-                                      bool aIsConference,
-                                      bool* aResult)
+                                      bool aIsConference)
 {
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   hfp->HandleCallStateChanged(aCallIndex, aCallState, EmptyString(), aNumber,
                               aIsOutgoing, false);
-  *aResult = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyListener::SupplementaryServiceNotification(int32_t aCallIndex,
                                                     uint16_t aNotification)
 {
   return NS_OK;
@@ -112,33 +110,33 @@ BluetoothTelephonyListener::BluetoothTel
 {
   mTelephonyListener = new TelephonyListener();
 }
 
 bool
 BluetoothTelephonyListener::StartListening()
 {
   nsCOMPtr<nsITelephonyProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
-  nsresult rv = provider->RegisterTelephonyMsg(mTelephonyListener);
+  nsresult rv = provider->RegisterListener(mTelephonyListener);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 bool
 BluetoothTelephonyListener::StopListening()
 {
   nsCOMPtr<nsITelephonyProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
-  nsresult rv = provider->UnregisterTelephonyMsg(mTelephonyListener);
+  nsresult rv = provider->UnregisterListener(mTelephonyListener);
 
   return NS_FAILED(rv) ? false : true;
 }
 
 nsITelephonyListener*
 BluetoothTelephonyListener::GetListener()
 {
   return mTelephonyListener;
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -69,17 +69,17 @@ using namespace mozilla::dom::devicestor
 
 #include "nsDirectoryServiceDefs.h"
 
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   mIOThread = new LazyIdleThread(
     DEFAULT_THREAD_TIMEOUT_MS,
     NS_LITERAL_CSTRING("DeviceStorageUsedSpaceCache I/O"));
 
 }
 
 DeviceStorageUsedSpaceCache::~DeviceStorageUsedSpaceCache()
@@ -88,17 +88,17 @@ DeviceStorageUsedSpaceCache::~DeviceStor
 
 DeviceStorageUsedSpaceCache*
 DeviceStorageUsedSpaceCache::CreateOrGet()
 {
   if (sDeviceStorageUsedSpaceCache) {
     return sDeviceStorageUsedSpaceCache;
   }
 
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache();
   ClearOnShutdown(&sDeviceStorageUsedSpaceCache);
   return sDeviceStorageUsedSpaceCache;
 }
 
 TemporaryRef<DeviceStorageUsedSpaceCache::CacheEntry>
 DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName)
@@ -203,18 +203,17 @@ DeviceStorageTypeChecker::~DeviceStorage
 
 DeviceStorageTypeChecker*
 DeviceStorageTypeChecker::CreateOrGet()
 {
   if (sDeviceStorageTypeChecker) {
     return sDeviceStorageTypeChecker;
   }
 
-  NS_ASSERTION(NS_IsMainThread(),
-               "This can only be created on the main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIStringBundleService> stringService
     = mozilla::services::GetStringBundleService();
   if (!stringService) {
     return nullptr;
   }
 
   nsCOMPtr<nsIStringBundle> filterBundle;
@@ -468,17 +467,17 @@ public:
     , mType(aType)
   {
   }
 
   ~IOEventComplete() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(NS_IsMainThread());
     nsString data;
     CopyASCIItoUTF16(mType, data);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 
     obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
@@ -580,17 +579,17 @@ DeviceStorageFile::Init()
 // here. Directories which depend on the root directory of a volume
 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
 static void
 InitDirs()
 {
   if (sDirs) {
     return;
   }
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
   sDirs = new GlobalDirs;
   ClearOnShutdown(&sDirs);
 
   nsCOMPtr<nsIProperties> dirService
     = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   NS_ASSERTION(dirService, "Must have directory service");
 
 #if !defined(MOZ_WIDGET_GONK)
@@ -760,17 +759,17 @@ DeviceStorageFile::GetRootDirectoryForTy
   }
 
   // crash reports directory.
   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
     f = sDirs->crashes;
   }
 
   // in testing, we default all device storage types to a temp directory
-  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
+  if (f && sDirs->temp) {
     f = sDirs->temp;
   }
 
   if (f) {
     f->Clone(aFile);
   }
 }
 
@@ -993,17 +992,17 @@ DeviceStorageFile::Write(InfallibleTArra
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::Remove()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   if (!mFile) {
     return NS_ERROR_FAILURE;
   }
 
   bool check;
   nsresult rv = mFile->Exists(&check);
   if (NS_FAILED(rv)) {
@@ -1022,17 +1021,17 @@ DeviceStorageFile::Remove()
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "deleted");
   NS_DispatchToMainThread(iocomplete);
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::CalculateMimeType()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoCString mimeType;
   nsCOMPtr<nsIMIMEService> mimeService =
     do_GetService(NS_MIMESERVICE_CONTRACTID);
   if (mimeService) {
     nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
     if (NS_FAILED(rv)) {
       mimeType.Truncate();
@@ -1042,17 +1041,17 @@ DeviceStorageFile::CalculateMimeType()
 
   mMimeType = NS_ConvertUTF8toUTF16(mimeType);
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::CalculateSizeAndModifiedDate()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   int64_t fileSize;
   nsresult rv = mFile->GetFileSize(&fileSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLength = fileSize;
 
   PRTime modDate;
@@ -1338,16 +1337,18 @@ UnregisterForSDCardChanges(nsIObserver* 
   obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
 #endif
 }
 
 void
 nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
                                             const nsAString& aStorageName)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIFile> f;
   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
                                              aStorageName,
                                              getter_AddRefs(f));
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(this, "file-watcher-update", false);
   obs->AddObserver(this, "disk-space-watcher", false);
   mRootDirectory = f;
@@ -1382,17 +1383,17 @@ InterfaceToJsval(nsPIDOMWindow* aWindow,
   }
 
   return someJsVal;
 }
 
 JS::Value
 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(aWindow, "Null Window");
 
   if (!aFile) {
     return JSVAL_NULL;
   }
 
   if (aFile->mEditable) {
     // TODO - needs janv's file handle support.
@@ -1412,17 +1413,17 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, D
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, aFile->mMimeType,
                                                 aFile->mLength, aFile->mFile,
                                                 aFile->mLastModifiedDate);
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
 }
 
 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(aWindow, "Null Window");
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
   }
 
   nsIScriptContext *scriptContext = sgo->GetScriptContext();
@@ -1505,17 +1506,17 @@ public:
   {
     CopyASCIItoUTF16(aMessage, mError);
   }
 
   ~PostErrorEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(NS_IsMainThread());
     mRequest->FireError(mError);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DOMRequest> mRequest;
   nsString mError;
@@ -1529,17 +1530,17 @@ ContinueCursorEvent::ContinueCursorEvent
 ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest)
   : mRequest(aRequest)
 {
 }
 
 already_AddRefed<DeviceStorageFile>
 ContinueCursorEvent::GetNextFile()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsDOMDeviceStorageCursor* cursor
     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
@@ -1624,17 +1625,17 @@ public:
     : mFile(aFile)
     , mRequest(aRequest)
   {
   }
 
   ~InitCursorEvent() {}
 
   NS_IMETHOD Run() {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     if (mFile->mFile) {
       bool check;
       mFile->mFile->IsDirectory(&check);
       if (!check) {
         nsCOMPtr<PostErrorEvent> event =
           new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
@@ -1818,17 +1819,17 @@ public:
     , mRequest(aRequest)
   {
   }
 
   ~PostAvailableResultEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("unavailable");
     if (mFile) {
       mFile->GetStatus(state);
     }
 
     AutoJSContext cx;
     JS::Rooted<JS::Value> result(cx,
@@ -1863,17 +1864,17 @@ public:
     : mValue(aValue)
     , mRequest(aRequest)
   {}
 
   ~PostResultEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(NS_IsMainThread());
 
     AutoJSContext cx;
     JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
     nsPIDOMWindow* window = mRequest->GetOwner();
 
     if (mFile) {
       result = nsIFileToJsval(window, mFile);
     } else if (mPath.Length()) {
@@ -1905,17 +1906,17 @@ public:
     , mFile(aFile)
     , mRequest(aRequest)
   {}
 
   ~WriteFileEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<nsIInputStream> stream;
     mBlob->GetInternalStream(getter_AddRefs(stream));
 
     bool check = false;
     mFile->mFile->Exists(&check);
     if (check) {
       nsCOMPtr<PostErrorEvent> event =
@@ -1959,17 +1960,17 @@ public:
   {
     mFile->CalculateMimeType();
   }
 
   ~ReadFileEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     nsRefPtr<nsRunnable> r;
     if (!mFile->mEditable) {
       bool check = false;
       mFile->mFile->Exists(&check);
       if (!check) {
         r = new PostErrorEvent(mRequest.forget(),
                                POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
@@ -2003,17 +2004,17 @@ public:
     : mFile(aFile)
     , mRequest(aRequest)
   {}
 
   ~DeleteFileEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
     mFile->Remove();
 
     nsRefPtr<nsRunnable> r;
     bool check = false;
     mFile->mFile->Exists(&check);
     if (check) {
       r = new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
@@ -2040,17 +2041,17 @@ public:
     : mFile(aFile)
     , mRequest(aRequest)
   {}
 
   ~UsedSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
     mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
                           &musicUsage, &totalUsage);
     nsCOMPtr<nsIRunnable> r;
     if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
       r = new PostResultEvent(mRequest.forget(), picturesUsage);
     }
@@ -2079,17 +2080,17 @@ public:
     : mFile(aFile)
     , mRequest(aRequest)
   {}
 
   ~FreeSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     int64_t freeSpace = 0;
     if (mFile) {
       mFile->GetDiskFreeSpace(&freeSpace);
     }
 
     nsCOMPtr<nsIRunnable> r;
     r = new PostResultEvent(mRequest.forget(),
@@ -2137,16 +2138,17 @@ public:
       , mRequest(aRequest)
       , mBlob(aBlob) {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
                                            nsIContentPermissionRequest)
 
   NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread());
 
     if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
       Allow();
       return NS_OK;
     }
 
     if (XRE_GetProcessType() == GeckoProcessType_Content) {
 
@@ -2543,17 +2545,17 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* 
 
 nsDOMDeviceStorage::~nsDOMDeviceStorage()
 {
 }
 
 void
 nsDOMDeviceStorage::Shutdown()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mStorageName.IsEmpty()) {
     UnregisterForSDCardChanges(this);
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->RemoveObserver(this, "file-watcher-update");
   obs->RemoveObserver(this, "disk-space-watcher");
@@ -3134,16 +3136,18 @@ nsDOMDeviceStorage::EnumerateEditable(co
 }
 
 
 already_AddRefed<DOMCursor>
 nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
                                       const EnumerationParameters& aOptions,
                                       bool aEditable, ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   PRTime since = 0;
   if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
@@ -3229,17 +3233,17 @@ nsDOMDeviceStorage::DispatchMountChangeE
 }
 #endif
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Observe(nsISupports *aSubject,
                             const char *aTopic,
                             const PRUnichar *aData)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!strcmp(aTopic, "file-watcher-update")) {
 
     DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
     Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
     return NS_OK;
   }
   if (!strcmp(aTopic, "disk-space-watcher")) {
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -35,17 +35,16 @@ DOM_SRCDIRS = \
   layout/xul/base/src \
   layout/xul/tree \
   dom/camera \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 DOM_SRCDIRS += \
   dom/system/gonk \
-  dom/telephony \
   dom/wifi \
   dom/icc/src \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_FM
 DOM_SRCDIRS += \
   dom/fmradio \
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -91,16 +91,17 @@
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 
 #include "mozilla/dom/indexedDB/PIndexedDBChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
+#include "mozilla/dom/telephony/TelephonyChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/PSpeechSynthesisChild.h"
 #endif
@@ -121,16 +122,17 @@
 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::mobilemessage;
 using namespace mozilla::dom::indexedDB;
+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)
 using namespace mozilla::system;
 #endif
@@ -914,16 +916,29 @@ ContentChild::AllocPSmsChild()
 
 bool
 ContentChild::DeallocPSmsChild(PSmsChild* aSms)
 {
     delete aSms;
     return true;
 }
 
+PTelephonyChild*
+ContentChild::AllocPTelephonyChild()
+{
+    MOZ_CRASH("No one should be allocating PTelephonyChild actors");
+}
+
+bool
+ContentChild::DeallocPTelephonyChild(PTelephonyChild* aActor)
+{
+    delete aActor;
+    return true;
+}
+
 PStorageChild*
 ContentChild::AllocPStorageChild()
 {
     NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
     return nullptr;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -147,16 +147,19 @@ public:
             const bool& aForceSave,
             const int64_t& aContentLength,
             const OptionalURIParams& aReferrer);
     virtual bool DeallocPExternalHelperAppChild(PExternalHelperAppChild *aService);
 
     virtual PSmsChild* AllocPSmsChild();
     virtual bool DeallocPSmsChild(PSmsChild*);
 
+    virtual PTelephonyChild* AllocPTelephonyChild();
+    virtual bool DeallocPTelephonyChild(PTelephonyChild*);
+
     virtual PStorageChild* AllocPStorageChild();
     virtual bool DeallocPStorageChild(PStorageChild* aActor);
 
     virtual PBluetoothChild* AllocPBluetoothChild();
     virtual bool DeallocPBluetoothChild(PBluetoothChild* aActor);
 
     virtual PFMRadioChild* AllocPFMRadioChild();
     virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #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/GeolocationBinding.h"
+#include "mozilla/dom/telephony/TelephonyParent.h"
 #include "SmsParent.h"
 #include "mozilla/Hal.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/net/NeckoParent.h"
@@ -143,16 +144,17 @@ static const char* sClipboardTextFlavors
 
 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::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;
 
 namespace mozilla {
 namespace dom {
@@ -2239,16 +2241,35 @@ ContentParent::AllocPSmsParent()
 
 bool
 ContentParent::DeallocPSmsParent(PSmsParent* aSms)
 {
     static_cast<SmsParent*>(aSms)->Release();
     return true;
 }
 
+PTelephonyParent*
+ContentParent::AllocPTelephonyParent()
+{
+    if (!AssertAppProcessPermission(this, "telephony")) {
+        return nullptr;
+    }
+
+    TelephonyParent* actor = new TelephonyParent();
+    NS_ADDREF(actor);
+    return actor;
+}
+
+bool
+ContentParent::DeallocPTelephonyParent(PTelephonyParent* aActor)
+{
+    static_cast<TelephonyParent*>(aActor)->Release();
+    return true;
+}
+
 PStorageParent*
 ContentParent::AllocPStorageParent()
 {
     return new DOMStorageDBParent();
 }
 
 bool
 ContentParent::DeallocPStorageParent(PStorageParent* aActor)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -316,16 +316,19 @@ private:
             const bool& aForceSave,
             const int64_t& aContentLength,
             const OptionalURIParams& aReferrer);
     virtual bool DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService);
 
     virtual PSmsParent* AllocPSmsParent();
     virtual bool DeallocPSmsParent(PSmsParent*);
 
+    virtual PTelephonyParent* AllocPTelephonyParent();
+    virtual bool DeallocPTelephonyParent(PTelephonyParent*);
+
     virtual PStorageParent* AllocPStorageParent();
     virtual bool DeallocPStorageParent(PStorageParent* aActor);
 
     virtual PBluetoothParent* AllocPBluetoothParent();
     virtual bool DeallocPBluetoothParent(PBluetoothParent* aActor);
     virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor);
 
     virtual PFMRadioParent* AllocPFMRadioParent();
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -15,16 +15,17 @@ include protocol PFMRadio;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
 include protocol PNecko;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
+include protocol PTelephony;
 include protocol PTestShell;
 include protocol PJavaScript;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
 
@@ -192,16 +193,17 @@ rpc protocol PContent
     manages PFMRadio;
     manages PHal;
     manages PIndexedDB;
     manages PMemoryReportRequest;
     manages PNecko;
     manages PSms;
     manages PSpeechSynthesis;
     manages PStorage;
+    manages PTelephony;
     manages PTestShell;
     manages PJavaScript;
 
 both:
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
@@ -348,16 +350,18 @@ parent:
     PNecko();
 
     PSms();
 
     PSpeechSynthesis();
 
     PStorage();
 
+    PTelephony();
+
     PBluetooth();
 
     PFMRadio();
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
--- a/dom/mobilemessage/src/Makefile.in
+++ b/dom/mobilemessage/src/Makefile.in
@@ -23,15 +23,14 @@ LOCAL_INCLUDES = \
   $(NULL)
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
 # subdirectory (and the ipc one).
 LOCAL_INCLUDES += $(VPATH:%=-I%)
 
 ifdef MOZ_B2G_RIL
 LOCAL_INCLUDES += \
-  -I$(topsrcdir)/dom/telephony \
   -I$(topsrcdir)/dom/system/gonk \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -67,25 +67,25 @@ PARALLEL_DIRS += [
     'indexedDB',
     'system',
     'ipc',
     'identity',
     'workers',
     'camera',
     'audiochannel',
     'promise',
-    'wappush'
+    'wappush',
+    'telephony',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     PARALLEL_DIRS += ['plugins/ipc/hangui']
 
 if CONFIG['MOZ_B2G_RIL']:
     PARALLEL_DIRS += [
-        'telephony',
         'wifi',
         'icc',
         'cellbroadcast',
         'voicemail',
     ]
 
 if CONFIG['MOZ_PAY']:
     PARALLEL_DIRS += ['payment']
--- a/dom/system/gonk/Makefile.in
+++ b/dom/system/gonk/Makefile.in
@@ -12,17 +12,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 include $(topsrcdir)/dom/dom-config.mk
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/src/geolocation \
-  -I$(topsrcdir)/dom/telephony \
   -I$(topsrcdir)/dom/wifi \
   -I$(topsrcdir)/dom/bluetooth \
   -I$(topsrcdir)/content/events/src \
   $(NULL)
 
 # TODO: Bug 908038, move this to moz.build
 WORKER_FILES := worker_buf.js \
   $(NULL)
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -66,27 +66,23 @@ const CELLBROADCASTETWSINFO_CID =
 const DOMMMIERROR_CID =
   Components.ID("{6b204c42-7928-4e71-89ad-f90cd82aff96}");
 
 const RIL_IPC_MSG_NAMES = [
   "RIL:CardStateChanged",
   "RIL:IccInfoChanged",
   "RIL:VoiceInfoChanged",
   "RIL:DataInfoChanged",
-  "RIL:EnumerateCalls",
   "RIL:GetAvailableNetworks",
   "RIL:NetworkSelectionModeChanged",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
-  "RIL:CallStateChanged",
   "RIL:EmergencyCbModeChanged",
   "RIL:VoicemailNotification",
   "RIL:VoicemailInfoChanged",
-  "RIL:CallError",
-  "RIL:SuppSvcNotification",
   "RIL:CardLockResult",
   "RIL:CardLockRetryCount",
   "RIL:USSDReceived",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:StkCommand",
   "RIL:StkSessionEnd",
   "RIL:DataError",
@@ -103,32 +99,26 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:CfStateChanged",
   "RIL:IccOpenChannel",
   "RIL:IccCloseChannel",
   "RIL:IccExchangeAPDU",
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact",
   "RIL:SetRoamingPreference",
   "RIL:GetRoamingPreference",
-  "RIL:CdmaCallWaiting",
   "RIL:ExitEmergencyCbMode",
   "RIL:SetVoicePrivacyMode",
   "RIL:GetVoicePrivacyMode",
-  "RIL:ConferenceCallStateChanged",
   "RIL:OtaStatusChanged"
 ];
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
-                                   "@mozilla.org/uuid-generator;1",
-                                   "nsIUUIDGenerator");
-
 function MobileIccCardLockResult(options) {
   this.lockType = options.lockType;
   this.enabled = options.enabled;
   this.retryCount = options.retryCount;
   this.success = options.success;
 }
 MobileIccCardLockResult.prototype = {
   __exposedProps__ : {lockType: 'r',
@@ -444,27 +434,25 @@ function RILContentHelper() {
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider,
                                          Ci.nsICellBroadcastProvider,
                                          Ci.nsIVoicemailProvider,
-                                         Ci.nsITelephonyProvider,
                                          Ci.nsIIccProvider,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
   classID:   RILCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
                                     classDescription: "RILContentHelper",
                                     interfaces: [Ci.nsIMobileConnectionProvider,
                                                  Ci.nsICellBroadcastProvider,
                                                  Ci.nsIVoicemailProvider,
-                                                 Ci.nsITelephonyProvider,
                                                  Ci.nsIIccProvider]}),
 
   // An utility function to copy objects.
   updateInfo: function updateInfo(srcInfo, destInfo) {
     for (let key in srcInfo) {
       destInfo[key] = srcInfo[key];
     }
   },
@@ -1276,21 +1264,19 @@ RILContentHelper.prototype = {
         requestId: requestId,
       }
     });
 
     return request;
   },
 
   _mobileConnectionListeners: null,
-  _telephonyListeners: null,
   _cellBroadcastListeners: null,
   _voicemailListeners: null,
   _iccListeners: null,
-  _enumerateTelephonyCallbacks: null,
 
   voicemailStatus: null,
 
   getVoicemailInfo: function getVoicemailInfo() {
     // Get voicemail infomation by IPC only on first time.
     this.getVoicemailInfo = function getVoicemailInfo() {
       return this.voicemailInfo;
     };
@@ -1342,34 +1328,16 @@ RILContentHelper.prototype = {
     this.registerListener("_mobileConnectionListeners", listener);
     cpmm.sendAsyncMessage("RIL:RegisterMobileConnectionMsg");
   },
 
   unregisterMobileConnectionMsg: function unregisteMobileConnectionMsg(listener) {
     this.unregisterListener("_mobileConnectionListeners", listener);
   },
 
-  registerTelephonyMsg: function registerTelephonyMsg(listener) {
-    debug("Registering for telephony-related messages");
-    this.registerListener("_telephonyListeners", listener);
-    cpmm.sendAsyncMessage("RIL:RegisterTelephonyMsg");
-  },
-
-  unregisterTelephonyMsg: function unregisteTelephonyMsg(listener) {
-    this.unregisterListener("_telephonyListeners", listener);
-
-    // We also need to make sure the listener is removed from
-    // _enumerateTelephonyCallbacks.
-    let index = this._enumerateTelephonyCallbacks.indexOf(listener);
-    if (index != -1) {
-      this._enumerateTelephonyCallbacks.splice(index, 1);
-      if (DEBUG) debug("Unregistered enumerateTelephony callback: " + listener);
-    }
-  },
-
   registerVoicemailMsg: function registerVoicemailMsg(listener) {
     debug("Registering for voicemail-related messages");
     this.registerListener("_voicemailListeners", listener);
     cpmm.sendAsyncMessage("RIL:RegisterVoicemailMsg");
   },
 
   unregisterVoicemailMsg: function unregisteVoicemailMsg(listener) {
     this.unregisterListener("_voicemailListeners", listener);
@@ -1390,145 +1358,16 @@ RILContentHelper.prototype = {
     this.registerListener("_iccListeners", listener);
     cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
   },
 
   unregisterIccMsg: function unregisterIccMsg(listener) {
     this.unregisterListener("_iccListeners", listener);
   },
 
-  enumerateCalls: function enumerateCalls(callback) {
-    debug("Requesting enumeration of calls for callback: " + callback);
-    // We need 'requestId' to meet the 'RILContentHelper <--> RadioInterfaceLayer'
-    // protocol.
-    let requestId = this._getRandomId();
-    cpmm.sendAsyncMessage("RIL:EnumerateCalls", {
-      clientId: 0,
-      data: {
-        requestId: requestId
-      }
-    });
-    if (!this._enumerateTelephonyCallbacks) {
-      this._enumerateTelephonyCallbacks = [];
-    }
-    this._enumerateTelephonyCallbacks.push(callback);
-  },
-
-  startTone: function startTone(dtmfChar) {
-    debug("Sending Tone for " + dtmfChar);
-    cpmm.sendAsyncMessage("RIL:StartTone", {
-      clientId: 0,
-      data: dtmfChar
-    });
-  },
-
-  stopTone: function stopTone() {
-    debug("Stopping Tone");
-    cpmm.sendAsyncMessage("RIL:StopTone", {clientId: 0});
-  },
-
-  dial: function dial(number) {
-    debug("Dialing " + number);
-    cpmm.sendAsyncMessage("RIL:Dial", {
-      clientId: 0,
-      data: number
-    });
-  },
-
-  dialEmergency: function dialEmergency(number) {
-    debug("Dialing emergency " + number);
-    cpmm.sendAsyncMessage("RIL:DialEmergency", {
-      clientId: 0,
-      data: number
-    });
-  },
-
-  hangUp: function hangUp(callIndex) {
-    debug("Hanging up call no. " + callIndex);
-    cpmm.sendAsyncMessage("RIL:HangUp", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  answerCall: function answerCall(callIndex) {
-    cpmm.sendAsyncMessage("RIL:AnswerCall", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  rejectCall: function rejectCall(callIndex) {
-    cpmm.sendAsyncMessage("RIL:RejectCall", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  holdCall: function holdCall(callIndex) {
-    cpmm.sendAsyncMessage("RIL:HoldCall", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  resumeCall: function resumeCall(callIndex) {
-    cpmm.sendAsyncMessage("RIL:ResumeCall", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  conferenceCall: function conferenceCall() {
-    cpmm.sendAsyncMessage("RIL:ConferenceCall", {
-      clientId: 0
-    });
-  },
-
-  separateCall: function separateCall(callIndex) {
-    cpmm.sendAsyncMessage("RIL:SeparateCall", {
-      clientId: 0,
-      data: callIndex
-    });
-  },
-
-  holdConference: function holdConference() {
-    cpmm.sendAsyncMessage("RIL:HoldConference", {
-      clientId: 0
-    });
-  },
-
-  resumeConference: function resumeConference() {
-    cpmm.sendAsyncMessage("RIL:ResumeConference", {
-      clientId: 0
-    });
-  },
-
-  get microphoneMuted() {
-    return cpmm.sendSyncMessage("RIL:GetMicrophoneMuted", {clientId: 0})[0];
-  },
-
-  set microphoneMuted(value) {
-    cpmm.sendAsyncMessage("RIL:SetMicrophoneMuted", {
-      clientId: 0,
-      data: value
-    });
-  },
-
-  get speakerEnabled() {
-    return cpmm.sendSyncMessage("RIL:GetSpeakerEnabled", {clientId: 0})[0];
-  },
-
-  set speakerEnabled(value) {
-    cpmm.sendAsyncMessage("RIL:SetSpeakerEnabled", {
-      clientId: 0,
-      data: value
-    });
-  },
-
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     if (topic == "xpcom-shutdown") {
       this.destroyDOMRequestHelper();
       Services.obs.removeObserver(this, "xpcom-shutdown");
     }
   },
@@ -1615,62 +1454,30 @@ RILContentHelper.prototype = {
                            "notifyDataChanged",
                            null);
         break;
       case "RIL:OtaStatusChanged":
         this._deliverEvent("_mobileConnectionListeners",
                            "notifyOtaStatusChanged",
                            [msg.json.data]);
         break;
-      case "RIL:EnumerateCalls":
-        this.handleEnumerateCalls(msg.json.calls);
-        break;
       case "RIL:GetAvailableNetworks":
         this.handleGetAvailableNetworks(msg.json);
         break;
       case "RIL:NetworkSelectionModeChanged":
         this.rilContext.networkSelectionMode = msg.json.data.mode;
         break;
       case "RIL:SelectNetwork":
         this.handleSelectNetwork(msg.json,
                                  RIL.GECKO_NETWORK_SELECTION_MANUAL);
         break;
       case "RIL:SelectNetworkAuto":
         this.handleSelectNetwork(msg.json,
                                  RIL.GECKO_NETWORK_SELECTION_AUTOMATIC);
         break;
-      case "RIL:CallStateChanged": {
-        let data = msg.json.data;
-        this._deliverEvent("_telephonyListeners",
-                           "callStateChanged",
-                           [data.callIndex, data.state,
-                            data.number, data.isActive,
-                            data.isOutgoing, data.isEmergency,
-                            data.isConference]);
-        break;
-      }
-      case "RIL:ConferenceCallStateChanged": {
-        let data = msg.json.data;
-        this._deliverEvent("_telephonyListeners",
-                           "conferenceCallStateChanged",
-                           [data]);
-        break;
-      }
-      case "RIL:CallError": {
-        let data = msg.json.data;
-        this._deliverEvent("_telephonyListeners",
-                           "notifyError",
-                           [data.callIndex, data.errorMsg]);
-        break;
-      }
-      case "RIL:SuppSvcNotification":
-        this._deliverEvent("_telephonyListeners",
-                           "supplementaryServiceNotification",
-                           [msg.json.callIndex, msg.json.notification]);
-        break;
       case "RIL:VoicemailNotification":
         this.handleVoicemailNotification(msg.json.data);
         break;
       case "RIL:VoicemailInfoChanged":
         this.updateInfo(msg.json.data, this.voicemailInfo);
         break;
       case "RIL:CardLockResult":
         if (msg.json.success) {
@@ -1781,21 +1588,16 @@ RILContentHelper.prototype = {
       }
       case "RIL:SetRoamingPreference":
         this.handleSimpleRequest(msg.json.requestId, msg.json.errorMsg, null);
         break;
       case "RIL:GetRoamingPreference":
         this.handleSimpleRequest(msg.json.requestId, msg.json.errorMsg,
                                  msg.json.mode);
         break;
-      case "RIL:CdmaCallWaiting":
-        this._deliverEvent("_telephonyListeners",
-                           "notifyCdmaCallWaiting",
-                           [msg.json.data]);
-        break;
       case "RIL:ExitEmergencyCbMode":
         this.handleExitEmergencyCbMode(msg.json);
         break;
       case "RIL:EmergencyCbModeChanged":
         let data = msg.json.data;
         this._deliverEvent("_mobileConnectionListeners",
                            "notifyEmergencyCbModeChanged",
                            [data.active, data.timeoutMs]);
@@ -1805,45 +1607,16 @@ RILContentHelper.prototype = {
         break;
       case "RIL:GetVoicePrivacyMode":
         this.handleSimpleRequest(msg.json.requestId, msg.json.errorMsg,
                                  msg.json.enabled);
         break;
     }
   },
 
-  handleEnumerateCalls: function handleEnumerateCalls(calls) {
-    debug("handleEnumerateCalls: " + JSON.stringify(calls));
-    let callback = this._enumerateTelephonyCallbacks.shift();
-    if (!calls.length) {
-      callback.enumerateCallStateComplete();
-      return;
-    }
-
-    for (let i in calls) {
-      let call = calls[i];
-      let keepGoing;
-      try {
-        keepGoing =
-          callback.enumerateCallState(call.callIndex, call.state, call.number,
-                                      call.isActive, call.isOutgoing,
-                                      call.isEmergency, call.isConference);
-      } catch (e) {
-        debug("callback handler for 'enumerateCallState' threw an " +
-              " exception: " + e);
-        keepGoing = true;
-      }
-      if (!keepGoing) {
-        break;
-      }
-    }
-
-    callback.enumerateCallStateComplete();
-  },
-
   handleSimpleRequest: function handleSimpleRequest(requestId, errorMsg, result) {
     if (errorMsg) {
       this.fireRequestError(requestId, errorMsg);
     } else {
       this.fireRequestSuccess(requestId, result);
     }
   },
 
@@ -2046,20 +1819,16 @@ RILContentHelper.prototype = {
       let mmiError = new this._window.DOMMMIError(result.serviceCode,
                                                   message.errorMsg,
                                                   null,
                                                   result.additionalInformation);
       Services.DOMRequest.fireDetailedError(request, mmiError);
     }
   },
 
-  _getRandomId: function _getRandomId() {
-    return gUUIDGenerator.generateUUID().toString();
-  },
-
   _deliverEvent: function _deliverEvent(listenerType, name, args) {
     let thisListeners = this[listenerType];
     if (!thisListeners) {
       return;
     }
 
     let listeners = thisListeners.slice();
     for (let listener of listeners) {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -41,19 +41,16 @@ function debug(s) {
 
 const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 const RADIOINTERFACE_CID =
   Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
 const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 
-const nsIAudioManager = Ci.nsIAudioManager;
-const nsITelephonyProvider = Ci.nsITelephonyProvider;
-
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSilentSmsReceivedObserverTopic    = "silent-sms-received";
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
@@ -68,40 +65,17 @@ const kCellBroadcastDisabled            
 const kPrefenceChangedObserverTopic      = "nsPref:changed";
 const kClirModePreference                = "ril.clirMode";
 
 const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
 const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
 
-const CALL_WAKELOCK_TIMEOUT              = 5000;
-const RADIO_POWER_OFF_TIMEOUT            = 30000;
-
-const RIL_IPC_TELEPHONY_MSG_NAMES = [
-  "RIL:EnumerateCalls",
-  "RIL:GetMicrophoneMuted",
-  "RIL:SetMicrophoneMuted",
-  "RIL:GetSpeakerEnabled",
-  "RIL:SetSpeakerEnabled",
-  "RIL:StartTone",
-  "RIL:StopTone",
-  "RIL:Dial",
-  "RIL:DialEmergency",
-  "RIL:HangUp",
-  "RIL:AnswerCall",
-  "RIL:RejectCall",
-  "RIL:HoldCall",
-  "RIL:ResumeCall",
-  "RIL:RegisterTelephonyMsg",
-  "RIL:ConferenceCall",
-  "RIL:SeparateCall",
-  "RIL:HoldConference",
-  "RIL:ResumeConference"
-];
+const RADIO_POWER_OFF_TIMEOUT = 30000;
 
 const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:SendMMI",
   "RIL:CancelMMI",
@@ -183,86 +157,32 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gTimeService",
                                    "@mozilla.org/time/timeservice;1",
                                    "nsITimeService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager",
                                    "@mozilla.org/telephony/system-worker-manager;1",
                                    "nsISystemWorkerManager");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyProvider",
+                                   "@mozilla.org/telephony/telephonyprovider;1",
+                                   "nsIGonkTelephonyProvider");
+
 XPCOMUtils.defineLazyGetter(this, "WAP", function () {
   let wap = {};
   Cu.import("resource://gre/modules/WapPushManager.js", wap);
   return wap;
 });
 
 XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function () {
   let ns = {};
   Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
   return ns.PhoneNumberUtils;
 });
 
-function convertRILCallState(state) {
-  switch (state) {
-    case RIL.CALL_STATE_ACTIVE:
-      return nsITelephonyProvider.CALL_STATE_CONNECTED;
-    case RIL.CALL_STATE_HOLDING:
-      return nsITelephonyProvider.CALL_STATE_HELD;
-    case RIL.CALL_STATE_DIALING:
-      return nsITelephonyProvider.CALL_STATE_DIALING;
-    case RIL.CALL_STATE_ALERTING:
-      return nsITelephonyProvider.CALL_STATE_ALERTING;
-    case RIL.CALL_STATE_INCOMING:
-    case RIL.CALL_STATE_WAITING:
-      return nsITelephonyProvider.CALL_STATE_INCOMING;
-    default:
-      throw new Error("Unknown rilCallState: " + state);
-  }
-}
-
-function convertRILSuppSvcNotification(notification) {
-  switch (notification) {
-    case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD:
-      return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD;
-    case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
-      return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED;
-    default:
-      throw new Error("Unknown rilSuppSvcNotification: " + notification);
-  }
-}
-
-/**
- * Fake nsIAudioManager implementation so that we can run the telephony
- * code in a non-Gonk build.
- */
-let FakeAudioManager = {
-  microphoneMuted: false,
-  masterVolume: 1.0,
-  masterMuted: false,
-  phoneState: nsIAudioManager.PHONE_STATE_CURRENT,
-  _forceForUse: {},
-  setForceForUse: function setForceForUse(usage, force) {
-    this._forceForUse[usage] = force;
-  },
-  getForceForUse: function setForceForUse(usage) {
-    return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE;
-  }
-};
-
-XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
-  try {
-    return Cc["@mozilla.org/telephony/audiomanager;1"]
-             .getService(nsIAudioManager);
-  } catch (ex) {
-    //TODO on the phone this should not fall back as silently.
-    if (DEBUG) debug("Using fake audio manager.");
-    return FakeAudioManager;
-  }
-});
-
 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
   return {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                            Ci.nsIObserver]),
 
     ril: null,
 
     // Manage message targets in terms of topic. Only the authorized and
@@ -285,38 +205,32 @@ XPCOMUtils.defineLazyGetter(this, "gMess
       this.ril = null;
 
       Services.obs.removeObserver(this, "xpcom-shutdown");
       this._unregisterMessageListeners();
     },
 
     _registerMessageListeners: function _registerMessageListeners() {
       ppmm.addMessageListener("child-process-shutdown", this);
-      for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) {
-        ppmm.addMessageListener(msgname, this);
-      }
       for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
         ppmm.addMessageListener(msgname, this);
       }
       for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) {
         ppmm.addMessageListener(msgName, this);
       }
       for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
         ppmm.addMessageListener(msgname, this);
       }
       for (let msgname of RIL_IPC_CELLBROADCAST_MSG_NAMES) {
         ppmm.addMessageListener(msgname, this);
       }
     },
 
     _unregisterMessageListeners: function _unregisterMessageListeners() {
       ppmm.removeMessageListener("child-process-shutdown", this);
-      for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) {
-        ppmm.removeMessageListener(msgname, this);
-      }
       for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
         ppmm.removeMessageListener(msgname, this);
       }
       for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) {
         ppmm.removeMessageListener(msgName, this);
       }
       for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
         ppmm.removeMessageListener(msgname, this);
@@ -425,25 +339,17 @@ XPCOMUtils.defineLazyGetter(this, "gMess
       if (msg.name == "child-process-shutdown") {
         // By the time we receive child-process-shutdown, the child process has
         // already forgotten its permissions so we need to unregister the target
         // for every permission.
         this._unregisterMessageTarget(null, msg.target);
         return;
       }
 
-      if (RIL_IPC_TELEPHONY_MSG_NAMES.indexOf(msg.name) != -1) {
-        if (!msg.target.assertPermission("telephony")) {
-          if (DEBUG) {
-            debug("Telephony message " + msg.name +
-                  " from a content process with no 'telephony' privileges.");
-          }
-          return null;
-        }
-      } else if (RIL_IPC_MOBILECONNECTION_MSG_NAMES.indexOf(msg.name) != -1) {
+      if (RIL_IPC_MOBILECONNECTION_MSG_NAMES.indexOf(msg.name) != -1) {
         if (!msg.target.assertPermission("mobileconnection")) {
           if (DEBUG) {
             debug("MobileConnection message " + msg.name +
                   " from a content process with no 'mobileconnection' privileges.");
           }
           return null;
         }
       } else if (RIL_IPC_ICCMANAGER_MSG_NAMES.indexOf(msg.name) != -1) {
@@ -471,19 +377,16 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           return null;
         }
       } else {
         if (DEBUG) debug("Ignoring unknown message type: " + msg.name);
         return null;
       }
 
       switch (msg.name) {
-        case "RIL:RegisterTelephonyMsg":
-          this._registerMessageTarget("telephony", msg.target);
-          return;
         case "RIL:RegisterMobileConnectionMsg":
           this._registerMessageTarget("mobileconnection", msg.target);
           return;
         case "RIL:RegisterIccMsg":
           this._registerMessageTarget("icc", msg.target);
           return;
         case "RIL:RegisterVoicemailMsg":
           this._registerMessageTarget("voicemail", msg.target);
@@ -514,23 +417,16 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           this._resendQueuedTargetMessage();
           break;
         case "xpcom-shutdown":
           this._shutdown();
           break;
       }
     },
 
-    sendTelephonyMessage: function sendTelephonyMessage(message, clientId, data) {
-      this._sendTargetMessage("telephony", message, {
-        clientId: clientId,
-        data: data
-      });
-    },
-
     sendMobileConnectionMessage: function sendMobileConnectionMessage(message, clientId, data) {
       this._sendTargetMessage("mobileconnection", message, {
         clientId: clientId,
         data: data
       });
     },
 
     sendVoicemailMessage: function sendVoicemailMessage(message, clientId, data) {
@@ -889,71 +785,16 @@ RadioInterface.prototype = {
   /**
    * Process a message from the content process.
    */
   receiveMessage: function receiveMessage(msg) {
     switch (msg.name) {
       case "RIL:GetRilContext":
         // This message is sync.
         return this.rilContext;
-      case "RIL:EnumerateCalls":
-        this.enumerateCalls(msg.target, msg.json.data);
-        break;
-      case "RIL:GetMicrophoneMuted":
-        // This message is sync.
-        return this.microphoneMuted;
-      case "RIL:SetMicrophoneMuted":
-        this.microphoneMuted = msg.json.data;
-        break;
-      case "RIL:GetSpeakerEnabled":
-        // This message is sync.
-        return this.speakerEnabled;
-      case "RIL:SetSpeakerEnabled":
-        this.speakerEnabled = msg.json.data;
-        break;
-      case "RIL:StartTone":
-        this.workerMessenger.send("startTone", { dtmfChar: msg.json.data });
-        break;
-      case "RIL:StopTone":
-        this.workerMessenger.send("stopTone");
-        break;
-      case "RIL:Dial":
-        this.dial(msg.json.data);
-        break;
-      case "RIL:DialEmergency":
-        this.dialEmergency(msg.json.data);
-        break;
-      case "RIL:HangUp":
-        this.workerMessenger.send("hangUp", { callIndex: msg.json.data });
-        break;
-      case "RIL:AnswerCall":
-        this.workerMessenger.send("answerCall", { callIndex: msg.json.data });
-        break;
-      case "RIL:RejectCall":
-        this.workerMessenger.send("rejectCall", { callIndex: msg.json.data });
-        break;
-      case "RIL:HoldCall":
-        this.workerMessenger.send("holdCall", { callIndex: msg.json.data });
-        break;
-      case "RIL:ResumeCall":
-        this.workerMessenger.send("resumeCall", { callIndex: msg.json.data });
-        break;
-      case "RIL:ConferenceCall":
-        this.workerMessenger.send("conferenceCall");
-        break;
-      case "RIL:SeparateCall":
-        this.workerMessenger.send("separateCall",
-                                  { callIndex: msg.json.data });
-        break;
-      case "RIL:HoldConference":
-        this.workerMessenger.send("holdConference");
-        break;
-      case "RIL:ResumeConference":
-        this.workerMessenger.send("resumeConference");
-        break;
       case "RIL:GetAvailableNetworks":
         this.workerMessenger.sendWithIPCMessage(msg, "getAvailableNetworks");
         break;
       case "RIL:SelectNetwork":
         this.workerMessenger.sendWithIPCMessage(msg, "selectNetwork");
         break;
       case "RIL:SelectNetworkAuto":
         this.workerMessenger.sendWithIPCMessage(msg, "selectNetworkAuto");
@@ -1053,38 +894,36 @@ RadioInterface.prototype = {
         this.workerMessenger.sendWithIPCMessage(msg, "queryVoicePrivacyMode");
         break;
     }
   },
 
   handleUnsolicitedWorkerMessage: function handleUnsolicitedWorkerMessage(message) {
     switch (message.rilMessageType) {
       case "callRing":
-        this.handleCallRing();
+        gTelephonyProvider.notifyCallRing();
         break;
       case "callStateChange":
-        // This one will handle its own notifications.
-        this.handleCallStateChange(message.call);
+        gTelephonyProvider.notifyCallStateChanged(message.call);
         break;
       case "callDisconnected":
-        // This one will handle its own notifications.
-        this.handleCallDisconnected(message.call);
+        gTelephonyProvider.notifyCallDisconnected(message.call);
         break;
       case "conferenceCallStateChanged":
-        this.handleConferenceCallStateChanged(message.state);
+        gTelephonyProvider.notifyConferenceCallStateChanged(message.state);
         break;
       case "cdmaCallWaiting":
-        gMessageManager.sendTelephonyMessage("RIL:CdmaCallWaiting",
-                                             this.clientId, message.number);
+        gTelephonyProvider.notifyCdmaCallWaiting(message.number);
         break;
       case "callError":
-        this.handleCallError(message);
+        gTelephonyProvider.notifyCallError(message.callIndex, message.errorMsg);
         break;
       case "suppSvcNotification":
-        this.handleSuppSvcNotification(message);
+        gTelephonyProvider.notifySupplementaryService(message.callIndex,
+                                                      message.notification);
         break;
       case "emergencyCbModeChange":
         this.handleEmergencyCbModeChange(message);
         break;
       case "networkinfochanged":
         this.updateNetworkInfo(message);
         break;
       case "networkselectionmodechange":
@@ -1723,175 +1562,16 @@ RadioInterface.prototype = {
       return;
     }
 
     if (DEBUG) this.debug("Data call settings: connect data call.");
     this.setupDataCallByType("default");
   },
 
   /**
-   * Track the active call and update the audio system as its state changes.
-   */
-  _activeCall: null,
-  updateCallAudioState: function updateCallAudioState(options) {
-    if (options.conferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) {
-      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
-      if (this.speakerEnabled) {
-        gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
-                                     nsIAudioManager.FORCE_SPEAKER);
-      }
-      return;
-    }
-    if (options.conferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN ||
-        options.conferenceState === nsITelephonyProvider.CALL_STATE_HELD) {
-      if (!this._activeCall) {
-        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-      }
-      return;
-    }
-
-    if (!options.call) {
-      return;
-    }
-
-    if (options.call.isConference) {
-      if (this._activeCall && this._activeCall.callIndex == options.call.callIndex) {
-        this._activeCall = null;
-      }
-      return;
-    }
-
-    let call = options.call;
-    switch (call.state) {
-      case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through...
-      case nsITelephonyProvider.CALL_STATE_ALERTING:
-      case nsITelephonyProvider.CALL_STATE_CONNECTED:
-        call.isActive = true;
-        this._activeCall = call;
-        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
-        if (this.speakerEnabled) {
-          gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
-                                       nsIAudioManager.FORCE_SPEAKER);
-        }
-        if (DEBUG) {
-          this.debug("Active call, put audio system into PHONE_STATE_IN_CALL: "
-                     + gAudioManager.phoneState);
-        }
-        break;
-      case nsITelephonyProvider.CALL_STATE_INCOMING:
-        call.isActive = false;
-        if (!this._activeCall) {
-          // We can change the phone state into RINGTONE only when there's
-          // no active call.
-          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
-          if (DEBUG) {
-            this.debug("Incoming call, put audio system into " +
-                       "PHONE_STATE_RINGTONE: " + gAudioManager.phoneState);
-          }
-        }
-        break;
-      case nsITelephonyProvider.CALL_STATE_HELD: // Fall through...
-      case nsITelephonyProvider.CALL_STATE_DISCONNECTED:
-        call.isActive = false;
-        if (this._activeCall &&
-            this._activeCall.callIndex == call.callIndex) {
-          // Previously active call is not active now.
-          this._activeCall = null;
-        }
-
-        if (!this._activeCall) {
-          // No active call. Disable the audio.
-          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-          if (DEBUG) {
-            this.debug("No active call, put audio system into " +
-                       "PHONE_STATE_NORMAL: " + gAudioManager.phoneState);
-          }
-        }
-        break;
-    }
-  },
-
-  _callRingWakeLock: null,
-  _callRingWakeLockTimer: null,
-  _cancelCallRingWakeLockTimer: function _cancelCallRingWakeLockTimer() {
-    if (this._callRingWakeLockTimer) {
-      this._callRingWakeLockTimer.cancel();
-    }
-    if (this._callRingWakeLock) {
-      this._callRingWakeLock.unlock();
-      this._callRingWakeLock = null;
-    }
-  },
-
-  /**
-   * Handle an incoming call.
-   *
-   * Not much is known about this call at this point, but it's enough
-   * to start bringing up the Phone app already.
-   */
-  handleCallRing: function handleCallRing() {
-    if (!this._callRingWakeLock) {
-      this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu");
-    }
-    if (!this._callRingWakeLockTimer) {
-      this._callRingWakeLockTimer =
-        Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    }
-    this._callRingWakeLockTimer
-        .initWithCallback(this._cancelCallRingWakeLockTimer.bind(this),
-                          CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
-
-    gSystemMessenger.broadcastMessage("telephony-new-call", {});
-  },
-
-  /**
-   * Handle call state changes by updating our current state and the audio
-   * system.
-   */
-  handleCallStateChange: function handleCallStateChange(call) {
-    if (DEBUG) this.debug("handleCallStateChange: " + JSON.stringify(call));
-    call.state = convertRILCallState(call.state);
-
-    if (call.state == nsITelephonyProvider.CALL_STATE_DIALING) {
-      gSystemMessenger.broadcastMessage("telephony-new-call", {});
-    }
-    this.updateCallAudioState({call: call});
-    gMessageManager.sendTelephonyMessage("RIL:CallStateChanged",
-                                         this.clientId, call);
-  },
-
-  /**
-   * Handle call disconnects by updating our current state and the audio system.
-   */
-  handleCallDisconnected: function handleCallDisconnected(call) {
-    if (DEBUG) this.debug("handleCallDisconnected: " + JSON.stringify(call));
-    call.state = nsITelephonyProvider.CALL_STATE_DISCONNECTED;
-    let duration = ("started" in call && typeof call.started == "number") ?
-      new Date().getTime() - call.started : 0;
-    let data = {
-      number: call.number,
-      duration: duration,
-      direction: call.isOutgoing ? "outgoing" : "incoming"
-    };
-    gSystemMessenger.broadcastMessage("telephony-call-ended", data);
-    this.updateCallAudioState({call: call});
-    gMessageManager.sendTelephonyMessage("RIL:CallStateChanged",
-                                         this.clientId, call);
-  },
-
-  handleConferenceCallStateChanged: function handleConferenceCallStateChanged(state) {
-    debug("handleConferenceCallStateChanged: " + state);
-    state = state != null ? convertRILCallState(state) :
-                            nsITelephonyProvider.CALL_STATE_UNKNOWN;
-    this.updateCallAudioState({conferenceState: state});
-    gMessageManager.sendTelephonyMessage("RIL:ConferenceCallStateChanged",
-                                         this.clientId, state);
-  },
-
-  /**
    * Update network selection mode
    */
   updateNetworkSelectionMode: function updateNetworkSelectionMode(message) {
     if (DEBUG) this.debug("updateNetworkSelectionMode: " + JSON.stringify(message));
     this.rilContext.networkSelectionMode = message.mode;
     gMessageManager.sendMobileConnectionMessage("RIL:NetworkSelectionModeChanged",
                                                 this.clientId, message);
   },
@@ -1901,33 +1581,16 @@ RadioInterface.prototype = {
    */
   handleEmergencyCbModeChange: function handleEmergencyCbModeChange(message) {
     if (DEBUG) this.debug("handleEmergencyCbModeChange: " + JSON.stringify(message));
     gMessageManager.sendMobileConnectionMessage("RIL:EmergencyCbModeChanged",
                                                 this.clientId, message);
   },
 
   /**
-   * Handle call error.
-   */
-  handleCallError: function handleCallError(message) {
-    gMessageManager.sendTelephonyMessage("RIL:CallError",
-                                         this.clientId, message);
-  },
-
-  /**
-   * Handle supplementary service notification.
-   */
-  handleSuppSvcNotification: function handleSuppSvcNotification(message) {
-    message.notification = convertRILSuppSvcNotification(message.notification);
-    gMessageManager.sendTelephonyMessage("RIL:SuppSvcNotification",
-                                         this.clientId, message);
-  },
-
-  /**
    * Handle WDP port push PDU. Constructor WDP bearer information and deliver
    * to WapPushManager.
    *
    * @param message
    *        A SMS message.
    */
   handleSmsWdpPortPush: function handleSmsWdpPortPush(message) {
     if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
@@ -2304,18 +1967,16 @@ RadioInterface.prototype = {
           try {
             value = Services.prefs.getBoolPref(kCellBroadcastDisabled);
           } catch(e) {}
           this.workerMessenger.send("setCellBroadcastDisabled",
                                     { disabled: value });
         }
         break;
       case "xpcom-shutdown":
-        // Cancel the timer for the call-ring wake lock.
-        this._cancelCallRingWakeLockTimer();
         // Shutdown all RIL network interfaces
         for each (let apnSetting in this.apnSettings.byAPN) {
           if (apnSetting.iface) {
             apnSetting.iface.shutdown();
           }
         }
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
@@ -2455,66 +2116,16 @@ RadioInterface.prototype = {
     if (DEBUG) this.debug("Setting radio power to " + value);
     this.workerMessenger.send("setRadioPower", { on: value });
   },
 
   rilContext: null,
 
   // Handle phone functions of nsIRILContentHelper
 
-  enumerateCalls: function enumerateCalls(target, message) {
-    if (DEBUG) this.debug("Requesting enumeration of calls for callback");
-    this.workerMessenger.send("enumerateCalls", message, (function(response) {
-      for (let call of response.calls) {
-        call.state = convertRILCallState(call.state);
-        call.isActive = this._activeCall ?
-          call.callIndex == this._activeCall.callIndex : false;
-      }
-      target.sendAsyncMessage("RIL:EnumerateCalls", response);
-      return false;
-    }).bind(this));
-  },
-
-  _validateNumber: function _validateNumber(number) {
-    // note: isPlainPhoneNumber also accepts USSD and SS numbers
-    if (PhoneNumberUtils.isPlainPhoneNumber(number)) {
-      return true;
-    }
-
-    this.handleCallError({
-      callIndex: -1,
-      errorMsg: RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER]
-    });
-    if (DEBUG) {
-      this.debug("Number '" + number + "' doesn't seem to be a viable number." +
-                 " Drop.");
-    }
-
-    return false;
-  },
-
-  dial: function dial(number) {
-    if (DEBUG) this.debug("Dialing " + number);
-    number = PhoneNumberUtils.normalize(number);
-    if (this._validateNumber(number)) {
-      this.workerMessenger.send("dial", { number: number,
-                                          isDialEmergency: false });
-    }
-  },
-
-  dialEmergency: function dialEmergency(number) {
-    if (DEBUG) this.debug("Dialing emergency " + number);
-    // we don't try to be too clever here, as the phone is probably in the
-    // locked state. Let's just check if it's a number without normalizing
-    if (this._validateNumber(number)) {
-      this.workerMessenger.send("dial", { number: number,
-                                          isDialEmergency: true });
-    }
-  },
-
   _sendCfStateChanged: function _sendCfStateChanged(message) {
     gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged",
                                                 this.clientId, message);
   },
 
   _updateCallingLineIdRestrictionPref:
     function _updateCallingLineIdRestrictionPref(mode) {
     try {
@@ -2559,47 +2170,16 @@ RadioInterface.prototype = {
       if (response.success) {
         this._updateCallingLineIdRestrictionPref(response.clirMode);
       }
       target.sendAsyncMessage("RIL:SetCallingLineIdRestriction", response);
       return false;
     }).bind(this));
   },
 
-  get microphoneMuted() {
-    return gAudioManager.microphoneMuted;
-  },
-  set microphoneMuted(value) {
-    if (value == this.microphoneMuted) {
-      return;
-    }
-    gAudioManager.microphoneMuted = value;
-
-    if (!this._activeCall) {
-      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-    }
-  },
-
-  get speakerEnabled() {
-    return (gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION) ==
-            nsIAudioManager.FORCE_SPEAKER);
-  },
-  set speakerEnabled(value) {
-    if (value == this.speakerEnabled) {
-      return;
-    }
-    let force = value ? nsIAudioManager.FORCE_SPEAKER :
-                        nsIAudioManager.FORCE_NONE;
-    gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
-
-    if (!this._activeCall) {
-      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-    }
-  },
-
   /**
    * List of tuples of national language identifier pairs.
    *
    * TODO: Support static/runtime settings, see bug 733331.
    */
   enabledGsmTableTuples: [
     [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
   ],
@@ -3357,16 +2937,23 @@ RadioInterface.prototype = {
                                                  chappap: chappap,
                                                  pdptype: pdptype });
   },
 
   deactivateDataCall: function deactivateDataCall(cid, reason) {
     this.workerMessenger.send("deactivateDataCall", { cid: cid,
                                                       reason: reason });
   },
+
+  sendWorkerMessage: function sendWorkerMessage(rilMessageType, message,
+                                                callback) {
+    this.workerMessenger.send(rilMessageType, message, function (response) {
+      return callback.handleResponse(response);
+    });
+  }
 };
 
 function RILNetworkInterface(radioInterface, apnSetting) {
   this.radioInterface = radioInterface;
   this.apnSetting = apnSetting;
 
   this.connectedTypes = [];
 }
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -73,17 +73,23 @@ interface nsIRilContext : nsISupports
 
   readonly attribute nsIDOMMozIccInfo iccInfo;
 
   readonly attribute nsIDOMMozMobileConnectionInfo voice;
 
   readonly attribute nsIDOMMozMobileConnectionInfo data;
 };
 
-[scriptable, uuid(a50d65aa-00da-11e3-b954-7bfb233d98fc)]
+[scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
+interface nsIRilSendWorkerMessageCallback : nsISupports
+{
+  boolean handleResponse(in jsval response);
+};
+
+[scriptable, uuid(61a8ca67-6113-4cd0-b443-e045f09863ed)]
 interface nsIRadioInterface : nsISupports
 {
   readonly attribute nsIRilContext rilContext;
 
   /**
    * PDP APIs
    */
   void setupDataCallByType(in DOMString apntype);
@@ -100,16 +106,20 @@ interface nsIRadioInterface : nsISupport
    */
   void getSegmentInfoForText(in DOMString text,
                              in nsIMobileMessageCallback request);
 
   void sendSMS(in DOMString number,
                in DOMString message,
                in boolean silent,
                in nsIMobileMessageCallback request);
+
+  void sendWorkerMessage(in DOMString type,
+              [optional] in jsval message,
+              [optional] in nsIRilSendWorkerMessageCallback callback);
 };
 
 [scriptable, uuid(44b03951-1444-4c03-bd37-0bcb3a01b56f)]
 interface nsIRadioInterfaceLayer : nsISupports
 {
   readonly attribute unsigned long numRadioInterfaces;
 
   nsIRadioInterface getRadioInterface(in long clientId);
--- a/dom/telephony/CallEvent.cpp
+++ b/dom/telephony/CallEvent.cpp
@@ -6,16 +6,17 @@
 
 #include "CallEvent.h"
 #include "mozilla/dom/CallEventBinding.h"
 
 #include "TelephonyCall.h"
 
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom;
+using mozilla::ErrorResult;
 
 /* static */
 already_AddRefed<CallEvent>
 CallEvent::Create(EventTarget* aOwner, const nsAString& aType,
                   TelephonyCall* aCall, bool aCanBubble,
                   bool aCancelable)
 {
   nsRefPtr<CallEvent> event = new CallEvent(aOwner, nullptr, nullptr);
--- a/dom/telephony/Makefile.in
+++ b/dom/telephony/Makefile.in
@@ -1,5 +1,7 @@
 # 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 $(topsrcdir)/dom/dom-config.mk
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -19,20 +19,19 @@
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
 #include "CallEvent.h"
 #include "CallsList.h"
 #include "TelephonyCall.h"
 #include "TelephonyCallGroup.h"
 
-#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
-
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom;
+using mozilla::ErrorResult;
 
 namespace {
 
 typedef nsAutoTArray<Telephony*, 2> TelephonyList;
 
 TelephonyList* gTelephonyList;
 
 } // anonymous namespace
@@ -46,16 +45,18 @@ public:
   NS_FORWARD_SAFE_NSITELEPHONYLISTENER(mTelephony)
 
   Listener(Telephony* aTelephony)
     : mTelephony(aTelephony)
   {
     MOZ_ASSERT(mTelephony);
   }
 
+  virtual ~Listener() {}
+
   void
   Disconnect()
   {
     MOZ_ASSERT(mTelephony);
     mTelephony = nullptr;
   }
 };
 
@@ -106,17 +107,17 @@ Telephony::~Telephony()
 
 void
 Telephony::Shutdown()
 {
   if (mListener) {
     mListener->Disconnect();
 
     if (mProvider) {
-      mProvider->UnregisterTelephonyMsg(mListener);
+      mProvider->UnregisterListener(mListener);
       mProvider = nullptr;
     }
 
     mListener = nullptr;
   }
 }
 
 JSObject*
@@ -127,17 +128,17 @@ Telephony::WrapObject(JSContext* aCx, JS
 
 // static
 already_AddRefed<Telephony>
 Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv)
 {
   NS_ASSERTION(aOwner, "Null owner!");
 
   nsCOMPtr<nsITelephonyProvider> ril =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   if (!ril) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
   if (!sgo) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -160,17 +161,17 @@ Telephony::Create(nsPIDOMWindow* aOwner,
   telephony->mGroup = TelephonyCallGroup::Create(telephony);
 
   nsresult rv = ril->EnumerateCalls(telephony->mListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  rv = ril->RegisterTelephonyMsg(telephony->mListener);
+  rv = ril->RegisterListener(telephony->mListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return telephony.forget();
 }
 
@@ -217,22 +218,17 @@ Telephony::DialInternal(bool isEmergency
       // One call has been dialed already and we only support one outgoing call
       // at a time.
       NS_WARNING("Only permitted to dial one call at a time!");
       aRv.Throw(NS_ERROR_NOT_AVAILABLE);
       return nullptr;
     }
   }
 
-  nsresult rv;
-  if (isEmergency) {
-    rv = mProvider->DialEmergency(aNumber);
-  } else {
-    rv = mProvider->Dial(aNumber);
-  }
+  nsresult rv = mProvider->Dial(aNumber, isEmergency);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   nsRefPtr<TelephonyCall> call = CreateNewDialingCall(aNumber);
 
   // Notify other telephony objects that we just dialed.
@@ -586,48 +582,45 @@ Telephony::EnumerateCallStateComplete()
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState,
                               const nsAString& aNumber, bool aIsActive,
                               bool aIsOutgoing, bool aIsEmergency,
-                              bool aIsConference, bool* aContinue)
+                              bool aIsConference)
 {
   nsRefPtr<TelephonyCall> call;
 
   // We request calls enumeration in constructor, and the asynchronous result
   // will be sent back through the callback function EnumerateCallState().
   // However, it is likely to have call state changes, i.e. CallStateChanged()
   // being called, before the enumeration result comes back. We'd make sure
   // we don't somehow add duplicates due to the race condition.
   call = aIsConference ? mGroup->GetCall(aCallIndex) : GetCall(aCallIndex);
   if (call) {
     // We have the call either in mCalls or in mGroup. Skip it.
-    *aContinue = true;
     return NS_OK;
   }
 
   if (MoveCall(aCallIndex, aIsConference)) {
-    *aContinue = true;
     return NS_OK;
   }
 
   // Didn't know anything about this call before now.
 
   call = TelephonyCall::Create(this, aNumber, aCallState, aCallIndex,
                                aIsEmergency, aIsConference);
   NS_ASSERTION(call, "This should never fail!");
 
   NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) :
                                mCalls.Contains(call),
                "Should have auto-added new call!");
 
-  *aContinue = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Telephony::SupplementaryServiceNotification(int32_t aCallIndex,
                                             uint16_t aNotification)
 {
   nsRefPtr<TelephonyCall> associatedCall;
--- a/dom/telephony/TelephonyCall.cpp
+++ b/dom/telephony/TelephonyCall.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/DOMError.h"
 
 #include "CallEvent.h"
 #include "Telephony.h"
 #include "TelephonyCallGroup.h"
 
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom;
+using mozilla::ErrorResult;
 
 // static
 already_AddRefed<TelephonyCall>
 TelephonyCall::Create(Telephony* aTelephony, const nsAString& aNumber,
                       uint16_t aCallState, uint32_t aCallIndex,
                       bool aEmergency, bool aIsConference)
 {
   NS_ASSERTION(aTelephony, "Null pointer!");
--- a/dom/telephony/TelephonyCallGroup.cpp
+++ b/dom/telephony/TelephonyCallGroup.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/dom/TelephonyCallGroupBinding.h"
 
 #include "CallEvent.h"
 #include "CallsList.h"
 #include "Telephony.h"
 
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom;
+using mozilla::ErrorResult;
 
 TelephonyCallGroup::TelephonyCallGroup()
 : mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN)
 {
   SetIsDOMBinding();
 }
 
 TelephonyCallGroup::~TelephonyCallGroup()
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"
+#ifdef MOZ_WIDGET_GONK
+#include "nsIGonkTelephonyProvider.h"
+#endif
+#include "nsServiceManagerUtils.h"
+#include "nsXULAppAPI.h"
+#include "ipc/TelephonyIPCProvider.h"
+
+USING_TELEPHONY_NAMESPACE
+
+/* static */ already_AddRefed<nsITelephonyProvider>
+TelephonyFactory::CreateTelephonyProvider()
+{
+  nsCOMPtr<nsITelephonyProvider> provider;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    provider = new TelephonyIPCProvider();
+#ifdef MOZ_WIDGET_GONK
+  } else {
+    provider = do_CreateInstance(GONK_TELEPHONY_PROVIDER_CONTRACTID);
+#endif
+  }
+
+  return provider.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 nsITelephonyProvider;
+
+BEGIN_TELEPHONY_NAMESPACE
+
+class TelephonyFactory
+{
+public:
+  static already_AddRefed<nsITelephonyProvider> CreateTelephonyProvider();
+};
+
+END_TELEPHONY_NAMESPACE
+
+#endif // mozilla_dom_telephony_TelephonyFactory_h
new file mode 100644
--- /dev/null
+++ b/dom/telephony/gonk/TelephonyProvider.js
@@ -0,0 +1,525 @@
+/* -*- Mode: js; 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/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+var RIL = {};
+Cu.import("resource://gre/modules/ril_consts.js", RIL);
+
+const GONK_TELEPHONYPROVIDER_CONTRACTID =
+  "@mozilla.org/telephony/gonktelephonyprovider;1";
+const GONK_TELEPHONYPROVIDER_CID =
+  Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}");
+
+const kPrefenceChangedObserverTopic = "nsPref:changed";
+const kXpcomShutdownObserverTopic   = "xpcom-shutdown";
+
+const nsIAudioManager = Ci.nsIAudioManager;
+const nsITelephonyProvider = Ci.nsITelephonyProvider;
+
+const CALL_WAKELOCK_TIMEOUT = 5000;
+
+let DEBUG;
+function debug(s) {
+  dump("TelephonyProvider: " + s + "\n");
+}
+
+XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
+  try {
+    return Cc["@mozilla.org/telephony/audiomanager;1"]
+             .getService(nsIAudioManager);
+  } catch (ex) {
+    //TODO on the phone this should not fall back as silently.
+
+    // Fake nsIAudioManager implementation so that we can run the telephony
+    // code in a non-Gonk build.
+    if (DEBUG) debug("Using fake audio manager.");
+    return {
+      microphoneMuted: false,
+      masterVolume: 1.0,
+      masterMuted: false,
+      phoneState: nsIAudioManager.PHONE_STATE_CURRENT,
+      _forceForUse: {},
+
+      setForceForUse: function setForceForUse(usage, force) {
+        this._forceForUse[usage] = force;
+      },
+
+      getForceForUse: function setForceForUse(usage) {
+        return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE;
+      }
+    };
+  }
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
+                                   "@mozilla.org/power/powermanagerservice;1",
+                                   "nsIPowerManagerService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
+XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () {
+  let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]);
+  // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS
+  return ril.getRadioInterface(0);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function () {
+  let ns = {};
+  Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
+  return ns.PhoneNumberUtils;
+});
+
+function TelephonyProvider() {
+  this._listeners = [];
+
+  this._updateDebugFlag();
+
+  Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false);
+  Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
+}
+TelephonyProvider.prototype = {
+  classID: GONK_TELEPHONYPROVIDER_CID,
+  classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYPROVIDER_CID,
+                                    contractID: GONK_TELEPHONYPROVIDER_CONTRACTID,
+                                    classDescription: "TelephonyProvider",
+                                    interfaces: [Ci.nsITelephonyProvider,
+                                                 Ci.nsIGonkTelephonyProvider],
+                                    flags: Ci.nsIClassInfo.SINGLETON}),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyProvider,
+                                         Ci.nsIGonkTelephonyProvider,
+                                         Ci.nsIObserver]),
+
+  _callRingWakeLock: null,
+  _callRingWakeLockTimer: null,
+  _cancelCallRingWakeLockTimer: function _cancelCallRingWakeLockTimer() {
+    if (this._callRingWakeLockTimer) {
+      this._callRingWakeLockTimer.cancel();
+    }
+    if (this._callRingWakeLock) {
+      this._callRingWakeLock.unlock();
+      this._callRingWakeLock = null;
+    }
+  },
+
+  // An array of nsITelephonyListener instances.
+  _listeners: null,
+  _notifyAllListeners: function _notifyAllListeners(aMethodName, aArgs) {
+    let listeners = this._listeners.slice();
+    for (let listener of listeners) {
+      if (this._listeners.indexOf(listener) == -1) {
+        // Listener has been unregistered in previous run.
+        continue;
+      }
+
+      let handler = listener[aMethodName];
+      try {
+        handler.apply(listener, aArgs);
+      } catch (e) {
+        debug("listener for " + aMethodName + " threw an exception: " + e);
+      }
+    }
+  },
+
+  /**
+   * Track the active call and update the audio system as its state changes.
+   */
+  _activeCall: null,
+  _updateCallAudioState: function _updateCallAudioState(aCall,
+                                                        aConferenceState) {
+    if (aConferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) {
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
+      if (this.speakerEnabled) {
+        gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
+                                     nsIAudioManager.FORCE_SPEAKER);
+      }
+      return;
+    }
+    if (aConferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN ||
+        aConferenceState === nsITelephonyProvider.CALL_STATE_HELD) {
+      if (!this._activeCall) {
+        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
+      }
+      return;
+    }
+
+    if (!aCall) {
+      return;
+    }
+
+    if (aCall.isConference) {
+      if (this._activeCall && this._activeCall.callIndex == aCall.callIndex) {
+        this._activeCall = null;
+      }
+      return;
+    }
+
+    switch (aCall.state) {
+      case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through...
+      case nsITelephonyProvider.CALL_STATE_ALERTING:
+      case nsITelephonyProvider.CALL_STATE_CONNECTED:
+        aCall.isActive = true;
+        this._activeCall = aCall;
+        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
+        if (this.speakerEnabled) {
+          gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
+                                       nsIAudioManager.FORCE_SPEAKER);
+        }
+        if (DEBUG) {
+          debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
+                gAudioManager.phoneState);
+        }
+        break;
+
+      case nsITelephonyProvider.CALL_STATE_INCOMING:
+        aCall.isActive = false;
+        if (!this._activeCall) {
+          // We can change the phone state into RINGTONE only when there's
+          // no active call.
+          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
+          if (DEBUG) {
+            debug("Incoming call, put audio system into PHONE_STATE_RINGTONE: " +
+                  gAudioManager.phoneState);
+          }
+        }
+        break;
+
+      case nsITelephonyProvider.CALL_STATE_HELD: // Fall through...
+      case nsITelephonyProvider.CALL_STATE_DISCONNECTED:
+        aCall.isActive = false;
+        if (this._activeCall &&
+            this._activeCall.callIndex == aCall.callIndex) {
+          // Previously active call is not active now.
+          this._activeCall = null;
+        }
+
+        if (!this._activeCall) {
+          // No active call. Disable the audio.
+          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
+          if (DEBUG) {
+            debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
+                  gAudioManager.phoneState);
+          }
+        }
+        break;
+    }
+  },
+
+  _convertRILCallState: function _convertRILCallState(aState) {
+    switch (aState) {
+      case RIL.CALL_STATE_ACTIVE:
+        return nsITelephonyProvider.CALL_STATE_CONNECTED;
+      case RIL.CALL_STATE_HOLDING:
+        return nsITelephonyProvider.CALL_STATE_HELD;
+      case RIL.CALL_STATE_DIALING:
+        return nsITelephonyProvider.CALL_STATE_DIALING;
+      case RIL.CALL_STATE_ALERTING:
+        return nsITelephonyProvider.CALL_STATE_ALERTING;
+      case RIL.CALL_STATE_INCOMING:
+      case RIL.CALL_STATE_WAITING:
+        return nsITelephonyProvider.CALL_STATE_INCOMING;
+      default:
+        throw new Error("Unknown rilCallState: " + aState);
+    }
+  },
+
+  _convertRILSuppSvcNotification: function _convertRILSuppSvcNotification(aNotification) {
+    switch (aNotification) {
+      case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD:
+        return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD;
+      case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
+        return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED;
+      default:
+        throw new Error("Unknown rilSuppSvcNotification: " + aNotification);
+    }
+  },
+
+  _validateNumber: function _validateNumber(aNumber) {
+    // note: isPlainPhoneNumber also accepts USSD and SS numbers
+    if (gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
+      return true;
+    }
+
+    let errorMsg = RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER];
+    let currentThread = Services.tm.currentThread;
+    currentThread.dispatch(this.notifyCallError.bind(this, -1, errorMsg),
+                           Ci.nsIThread.DISPATCH_NORMAL);
+    if (DEBUG) {
+      debug("Number '" + aNumber + "' doesn't seem to be a viable number. Drop.");
+    }
+
+    return false;
+  },
+
+  _updateDebugFlag: function _updateDebugFlag() {
+    try {
+      DEBUG = RIL.DEBUG_RIL ||
+              Services.prefs.getBoolPref("ril.debugging.enabled");
+    } catch (e) {}
+  },
+
+  /**
+   * nsITelephonyProvider interface.
+   */
+
+  registerListener: function(aListener) {
+    if (this._listeners.indexOf(aListener) >= 0) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    this._listeners.push(aListener);
+  },
+
+  unregisterListener: function(aListener) {
+    let index = this._listeners.indexOf(aListener);
+    if (index < 0) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    this._listeners.splice(index, 1);
+  },
+
+  enumerateCalls: function(aListener) {
+    if (DEBUG) debug("Requesting enumeration of calls for callback");
+    gRadioInterface.sendWorkerMessage("enumerateCalls", null,
+                                      (function(response) {
+      for (let call of response.calls) {
+        call.state = this._convertRILCallState(call.state);
+        call.isActive = this._activeCall ?
+          (call.callIndex == this._activeCall.callIndex) : false;
+
+        aListener.enumerateCallState(call.callIndex, call.state, call.number,
+                                     call.isActive, call.isOutgoing,
+                                     call.isEmergency, call.isConference);
+      }
+      aListener.enumerateCallStateComplete();
+
+      return false;
+    }).bind(this));
+  },
+
+  dial: function(aNumber, aIsEmergency) {
+    if (DEBUG) debug("Dialing " + (aIsEmergency ? "emergency " : "") + aNumber);
+    // we don't try to be too clever here, as the phone is probably in the
+    // locked state. Let's just check if it's a number without normalizing
+    if (!aIsEmergency) {
+      aNumber = gPhoneNumberUtils.normalize(aNumber);
+    }
+    if (this._validateNumber(aNumber)) {
+      gRadioInterface.sendWorkerMessage("dial", { number: aNumber,
+                                                  isDialEmergency: aIsEmergency });
+    }
+  },
+
+  hangUp: function(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("hangUp", { callIndex: aCallIndex });
+  },
+
+  startTone: function(aDtmfChar) {
+    gRadioInterface.sendWorkerMessage("startTone", { dtmfChar: aDtmfChar });
+  },
+
+  stopTone: function() {
+    gRadioInterface.sendWorkerMessage("stopTone");
+  },
+
+  answerCall: function(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("answerCall", { callIndex: aCallIndex });
+  },
+
+  rejectCall: function(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("rejectCall", { callIndex: aCallIndex });
+  },
+
+  holdCall: function(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("holdCall", { callIndex: aCallIndex });
+  },
+
+  resumeCall: function(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("resumeCall", { callIndex: aCallIndex });
+  },
+
+  conferenceCall: function conferenceCall() {
+    gRadioInterface.sendWorkerMessage("conferenceCall");
+  },
+
+  separateCall: function separateCall(aCallIndex) {
+    gRadioInterface.sendWorkerMessage("separateCall", { callIndex: aCallIndex });
+  },
+
+  holdConference: function holdConference() {
+    gRadioInterface.sendWorkerMessage("holdConference");
+  },
+
+  resumeConference: function resumeConference() {
+    gRadioInterface.sendWorkerMessage("resumeConference");
+  },
+
+  get microphoneMuted() {
+    return gAudioManager.microphoneMuted;
+  },
+
+  set microphoneMuted(aMuted) {
+    if (aMuted == this.microphoneMuted) {
+      return;
+    }
+    gAudioManager.microphoneMuted = aMuted;
+
+    if (!this._activeCall) {
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
+    }
+  },
+
+  get speakerEnabled() {
+    let force = gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION);
+    return (force == nsIAudioManager.FORCE_SPEAKER);
+  },
+
+  set speakerEnabled(aEnabled) {
+    if (aEnabled == this.speakerEnabled) {
+      return;
+    }
+    let force = aEnabled ? nsIAudioManager.FORCE_SPEAKER :
+                           nsIAudioManager.FORCE_NONE;
+    gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
+
+    if (!this._activeCall) {
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
+    }
+  },
+
+  /**
+   * nsIGonkTelephonyProvider interface.
+   */
+
+  /**
+   * Handle call disconnects by updating our current state and the audio system.
+   */
+  notifyCallDisconnected: function notifyCallDisconnected(aCall) {
+    if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall));
+
+    aCall.state = nsITelephonyProvider.CALL_STATE_DISCONNECTED;
+    let duration = ("started" in aCall && typeof aCall.started == "number") ?
+      new Date().getTime() - aCall.started : 0;
+    let data = {
+      number: aCall.number,
+      duration: duration,
+      direction: aCall.isOutgoing ? "outgoing" : "incoming"
+    };
+    gSystemMessenger.broadcastMessage("telephony-call-ended", data);
+
+    this._updateCallAudioState(aCall, null);
+
+    this._notifyAllListeners("callStateChanged", [aCall.callIndex,
+                                                  aCall.state,
+                                                  aCall.number,
+                                                  aCall.isActive,
+                                                  aCall.isOutgoing,
+                                                  aCall.isEmergency,
+                                                  aCall.isConference]);
+  },
+
+  /**
+   * Handle call error.
+   */
+  notifyCallError: function notifyCallError(aCallIndex, aErrorMsg) {
+    this._notifyAllListeners("notifyError", [aCallIndex, aErrorMsg]);
+  },
+
+  /**
+   * Handle an incoming call.
+   *
+   * Not much is known about this call at this point, but it's enough
+   * to start bringing up the Phone app already.
+   */
+  notifyCallRing: function notifyCallRing() {
+    if (!this._callRingWakeLock) {
+      this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu");
+    }
+    if (!this._callRingWakeLockTimer) {
+      this._callRingWakeLockTimer =
+        Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    }
+    this._callRingWakeLockTimer
+        .initWithCallback(this._cancelCallRingWakeLockTimer.bind(this),
+                          CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
+
+    gSystemMessenger.broadcastMessage("telephony-new-call", {});
+  },
+
+  /**
+   * Handle call state changes by updating our current state and the audio
+   * system.
+   */
+  notifyCallStateChanged: function notifyCallStateChanged(aCall) {
+    if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
+
+    aCall.state = this._convertRILCallState(aCall.state);
+    if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) {
+      gSystemMessenger.broadcastMessage("telephony-new-call", {});
+    }
+
+    this._updateCallAudioState(aCall, null);
+
+    this._notifyAllListeners("callStateChanged", [aCall.callIndex,
+                                                  aCall.state,
+                                                  aCall.number,
+                                                  aCall.isActive,
+                                                  aCall.isOutgoing,
+                                                  aCall.isEmergency,
+                                                  aCall.isConference]);
+  },
+
+  notifyCdmaCallWaiting: function notifyCdmaCallWaiting(aNumber) {
+    this._notifyAllListeners("notifyCdmaCallWaiting", [aNumber]);
+  },
+
+  notifySupplementaryService: function notifySupplementaryService(aCallIndex,
+                                                                  aNotification) {
+    let notification = this._convertRILSuppSvcNotification(aNotification);
+    this._notifyAllListeners("supplementaryServiceNotification",
+                             [aCallIndex, notification]);
+  },
+
+  notifyConferenceCallStateChanged: function notifyConferenceCallStateChanged(aState) {
+    if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
+    aState = aState != null ? convertRILCallState(aState) :
+                              nsITelephonyProvider.CALL_STATE_UNKNOWN;
+    this._updateCallAudioState(null, aState);
+
+    this._notifyAllListeners("conferenceCallStateChanged", [aState]);
+  },
+
+  /**
+   * nsIObserver interface.
+   */
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case kPrefenceChangedObserverTopic:
+        if (aData === "ril.debugging.enabled") {
+          this._updateDebugFlag();
+        }
+        break;
+
+      case kXpcomShutdownObserverTopic:
+        // Cancel the timer for the call-ring wake lock.
+        this._cancelCallRingWakeLockTimer();
+
+        Services.obs.removeObserver(this, kPrefenceChangedObserverTopic);
+        Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
+        break;
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyProvider]);
new file mode 100644
--- /dev/null
+++ b/dom/telephony/gonk/TelephonyProvider.manifest
@@ -0,0 +1,2 @@
+component {67d26434-d063-4d28-9f48-5b3189788155} TelephonyProvider.js
+contract @mozilla.org/telephony/gonktelephonyprovider;1 {67d26434-d063-4d28-9f48-5b3189788155}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/PTelephony.ipdl
@@ -0,0 +1,83 @@
+/* -*- 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/. */
+
+include protocol PContent;
+include protocol PTelephonyRequest;
+include TelephonyTypes;
+
+namespace mozilla {
+namespace dom {
+namespace telephony {
+
+sync protocol PTelephony {
+  manager PContent;
+  manages PTelephonyRequest;
+
+child:
+  NotifyCallError(int32_t aCallIndex, nsString aError);
+
+  NotifyCallStateChanged(IPCCallStateData aData);
+
+  NotifyCdmaCallWaiting(nsString aNumber);
+
+  NotifyConferenceCallStateChanged(uint16_t aCallState);
+
+  NotifySupplementaryService(int32_t aCallIndex, uint16_t aNotification);
+
+parent:
+  /**
+   * Sent when the child no longer needs to use PTelephony.
+   */
+  __delete__();
+
+  /**
+   * Sent when the child makes an asynchronous request to the parent.  It's
+   * currently only for request call enumeration.
+   */
+  PTelephonyRequest();
+
+  RegisterListener();
+
+  UnregisterListener();
+
+  DialCall(nsString aNumber, bool aIsEmergency);
+
+  HangUpCall(uint32_t aCallIndex);
+
+  AnswerCall(uint32_t aCallIndex);
+
+  RejectCall(uint32_t aCallIndex);
+
+  HoldCall(uint32_t aCallIndex);
+
+  ResumeCall(uint32_t aCallIndex);
+
+  ConferenceCall();
+
+  SeparateCall(uint32_t aCallIndex);
+
+  HoldConference();
+
+  ResumeConference();
+
+  StartTone(nsString aTone);
+
+  StopTone();
+
+  sync GetMicrophoneMuted()
+    returns (bool aMuted);
+
+  SetMicrophoneMuted(bool aMuted);
+
+  sync GetSpeakerEnabled()
+    returns (bool aEnabled);
+
+  SetSpeakerEnabled(bool aEnabled);
+};
+
+} /* namespace telephony */
+} /* namespace dom */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/PTelephonyRequest.ipdl
@@ -0,0 +1,30 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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 protocol PTelephony;
+include TelephonyTypes;
+
+namespace mozilla {
+namespace dom {
+namespace telephony {
+
+protocol PTelephonyRequest
+{
+  manager PTelephony;
+
+child:
+  NotifyEnumerateCallState(IPCCallStateData aData);
+
+  /**
+   * Sent when the asynchronous request has completed. It's currently only for
+   * request call enumeration.
+   */
+  __delete__();
+};
+
+} /* namespace telephony */
+} /* namespace dom */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyChild.cpp
@@ -0,0 +1,130 @@
+/* -*- 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/TelephonyChild.h"
+
+USING_TELEPHONY_NAMESPACE
+
+/*******************************************************************************
+ * TelephonyChild
+ ******************************************************************************/
+
+TelephonyChild::TelephonyChild(nsITelephonyListener* aListener)
+  : mListener(aListener)
+{
+  MOZ_ASSERT(aListener);
+}
+
+void
+TelephonyChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mListener = nullptr;
+}
+
+PTelephonyRequestChild*
+TelephonyChild::AllocPTelephonyRequestChild()
+{
+  MOZ_CRASH("Caller is supposed to manually construct a request!");
+}
+
+bool
+TelephonyChild::DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+TelephonyChild::RecvNotifyCallError(const int32_t& aCallIndex,
+                                    const nsString& aError)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->NotifyError(aCallIndex, aError);
+  return true;
+}
+
+bool
+TelephonyChild::RecvNotifyCallStateChanged(const IPCCallStateData& aData)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->CallStateChanged(aData.callIndex(),
+                              aData.callState(),
+                              aData.number(),
+                              aData.isActive(),
+                              aData.isOutGoing(),
+                              aData.isEmergency(),
+                              aData.isConference());
+  return true;
+}
+
+bool
+TelephonyChild::RecvNotifyCdmaCallWaiting(const nsString& aNumber)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->NotifyCdmaCallWaiting(aNumber);
+  return true;
+}
+
+bool
+TelephonyChild::RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->ConferenceCallStateChanged(aCallState);
+  return true;
+}
+
+bool
+TelephonyChild::RecvNotifySupplementaryService(const int32_t& aCallIndex,
+                                               const uint16_t& aNotification)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->SupplementaryServiceNotification(aCallIndex, aNotification);
+  return true;
+}
+
+/*******************************************************************************
+ * TelephonyRequestChild
+ ******************************************************************************/
+
+TelephonyRequestChild::TelephonyRequestChild(nsITelephonyListener* aListener)
+  : mListener(aListener)
+{
+  MOZ_ASSERT(aListener);
+}
+
+void
+TelephonyRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mListener = nullptr;
+}
+
+bool
+TelephonyRequestChild::Recv__delete__()
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->EnumerateCallStateComplete();
+  return true;
+}
+
+bool
+TelephonyRequestChild::RecvNotifyEnumerateCallState(const IPCCallStateData& aData)
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->EnumerateCallState(aData.callIndex(),
+                                aData.callState(),
+                                aData.number(),
+                                aData.isActive(),
+                                aData.isOutGoing(),
+                                aData.isEmergency(),
+                                aData.isConference());
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyChild.h
@@ -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/. */
+
+#ifndef mozilla_dom_telephony_TelephonyChild_h
+#define mozilla_dom_telephony_TelephonyChild_h
+
+#include "mozilla/dom/telephony/TelephonyCommon.h"
+#include "mozilla/dom/telephony/PTelephonyChild.h"
+#include "mozilla/dom/telephony/PTelephonyRequestChild.h"
+#include "nsITelephonyProvider.h"
+
+BEGIN_TELEPHONY_NAMESPACE
+
+class TelephonyChild : public PTelephonyChild
+{
+public:
+  TelephonyChild(nsITelephonyListener* aListener);
+
+protected:
+  virtual ~TelephonyChild() {}
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PTelephonyRequestChild*
+  AllocPTelephonyRequestChild() MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyCallError(const int32_t& aCallIndex,
+                      const nsString& aError) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyCallStateChanged(const IPCCallStateData& aData) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyCdmaCallWaiting(const nsString& aNumber) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifySupplementaryService(const int32_t& aCallIndex,
+                                 const uint16_t& aNotification) MOZ_OVERRIDE;
+
+private:
+  nsCOMPtr<nsITelephonyListener> mListener;
+};
+
+class TelephonyRequestChild : public PTelephonyRequestChild
+{
+public:
+  TelephonyRequestChild(nsITelephonyListener* aListener);
+
+protected:
+  virtual ~TelephonyRequestChild() {}
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  Recv__delete__() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyEnumerateCallState(const IPCCallStateData& aData) MOZ_OVERRIDE;
+
+private:
+  nsCOMPtr<nsITelephonyListener> mListener;
+};
+
+END_TELEPHONY_NAMESPACE
+
+#endif // mozilla_dom_telephony_TelephonyChild_h
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyIPCProvider.cpp
@@ -0,0 +1,258 @@
+/* -*- 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/ContentChild.h"
+#include "mozilla/dom/telephony/TelephonyChild.h"
+#include "ipc/TelephonyIPCProvider.h"
+
+USING_TELEPHONY_NAMESPACE
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS2(TelephonyIPCProvider,
+                   nsITelephonyProvider,
+                   nsITelephonyListener)
+
+TelephonyIPCProvider::TelephonyIPCProvider()
+{
+  // Deallocated in ContentChild::DeallocPTelephonyChild().
+  mPTelephonyChild = new TelephonyChild(this);
+  ContentChild::GetSingleton()->SendPTelephonyConstructor(mPTelephonyChild);
+}
+
+TelephonyIPCProvider::~TelephonyIPCProvider()
+{
+  mPTelephonyChild->Send__delete__(mPTelephonyChild);
+  mPTelephonyChild = nullptr;
+}
+
+/*
+ * Implementation of nsITelephonyProvider.
+ */
+
+NS_IMETHODIMP
+TelephonyIPCProvider::RegisterListener(nsITelephonyListener *aListener)
+{
+  MOZ_ASSERT(!mListeners.Contains(aListener));
+
+  // nsTArray doesn't fail.
+  mListeners.AppendElement(aListener);
+
+  if (mListeners.Length() == 1) {
+    mPTelephonyChild->SendRegisterListener();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::UnregisterListener(nsITelephonyListener *aListener)
+{
+  MOZ_ASSERT(mListeners.Contains(aListener));
+
+  // We always have the element here, so it can't fail.
+  mListeners.RemoveElement(aListener);
+
+  if (!mListeners.Length()) {
+    mPTelephonyChild->SendUnregisterListener();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::EnumerateCalls(nsITelephonyListener *aListener)
+{
+  // Life time of newly allocated TelephonyRequestChild instance is managed by
+  // IPDL itself.
+  TelephonyRequestChild* actor = new TelephonyRequestChild(aListener);
+  mPTelephonyChild->SendPTelephonyRequestConstructor(actor);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::Dial(const nsAString& aNumber,
+                          bool aIsEmergency)
+{
+  mPTelephonyChild->SendDialCall(nsString(aNumber), aIsEmergency);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::HangUp(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendHangUpCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::AnswerCall(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendAnswerCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::RejectCall(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendRejectCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::HoldCall(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendHoldCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::ResumeCall(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendResumeCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::ConferenceCall()
+{
+  mPTelephonyChild->SendConferenceCall();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::SeparateCall(uint32_t aCallIndex)
+{
+  mPTelephonyChild->SendSeparateCall(aCallIndex);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::HoldConference()
+{
+  mPTelephonyChild->SendHoldConference();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::ResumeConference()
+{
+  mPTelephonyChild->SendResumeConference();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::StartTone(const nsAString& aDtmfChar)
+{
+  mPTelephonyChild->SendStartTone(nsString(aDtmfChar));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::StopTone()
+{
+  mPTelephonyChild->SendStopTone();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::GetMicrophoneMuted(bool* aMuted)
+{
+  mPTelephonyChild->SendGetMicrophoneMuted(aMuted);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::SetMicrophoneMuted(bool aMuted)
+{
+  mPTelephonyChild->SendSetMicrophoneMuted(aMuted);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::GetSpeakerEnabled(bool* aEnabled)
+{
+  mPTelephonyChild->SendGetSpeakerEnabled(aEnabled);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::SetSpeakerEnabled(bool aEnabled)
+{
+  mPTelephonyChild->SendSetSpeakerEnabled(aEnabled);
+  return NS_OK;
+}
+
+// nsITelephonyListener
+
+NS_IMETHODIMP
+TelephonyIPCProvider::CallStateChanged(uint32_t aCallIndex,
+                                      uint16_t aCallState,
+                                      const nsAString& aNumber,
+                                      bool aIsActive,
+                                      bool aIsOutgoing,
+                                      bool aIsEmergency,
+                                      bool aIsConference)
+{
+  for (uint32_t i = 0; i < mListeners.Length(); i++) {
+    mListeners[i]->CallStateChanged(aCallIndex, aCallState, aNumber,
+                                    aIsActive, aIsOutgoing, aIsEmergency,
+                                    aIsConference);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::ConferenceCallStateChanged(uint16_t aCallState)
+{
+  for (uint32_t i = 0; i < mListeners.Length(); i++) {
+    mListeners[i]->ConferenceCallStateChanged(aCallState);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::EnumerateCallStateComplete()
+{
+  MOZ_CRASH("Not a EnumerateCalls request!");
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::EnumerateCallState(uint32_t aCallIndex,
+                                        uint16_t aCallState,
+                                        const nsAString& aNumber,
+                                        bool aIsActive,
+                                        bool aIsOutgoing,
+                                        bool aIsEmergency,
+                                        bool aIsConference)
+{
+  MOZ_CRASH("Not a EnumerateCalls request!");
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::NotifyCdmaCallWaiting(const nsAString& aNumber)
+{
+  for (uint32_t i = 0; i < mListeners.Length(); i++) {
+    mListeners[i]->NotifyCdmaCallWaiting(aNumber);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::NotifyError(int32_t aCallIndex,
+                                 const nsAString& aError)
+{
+  for (uint32_t i = 0; i < mListeners.Length(); i++) {
+    mListeners[i]->NotifyError(aCallIndex, aError);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::SupplementaryServiceNotification(int32_t aCallIndex,
+                                                      uint16_t aNotification)
+{
+  for (uint32_t i = 0; i < mListeners.Length(); i++) {
+    mListeners[i]->SupplementaryServiceNotification(aCallIndex, aNotification);
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyIPCProvider.h
@@ -0,0 +1,37 @@
+/* -*- 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_TelephonyIPCProvider_h
+#define mozilla_dom_telephony_TelephonyIPCProvider_h
+
+#include "mozilla/dom/telephony/TelephonyCommon.h"
+#include "mozilla/Attributes.h"
+#include "nsITelephonyProvider.h"
+
+BEGIN_TELEPHONY_NAMESPACE
+
+class PTelephonyChild;
+
+class TelephonyIPCProvider MOZ_FINAL : public nsITelephonyProvider
+                                     , public nsITelephonyListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITELEPHONYPROVIDER
+  NS_DECL_NSITELEPHONYLISTENER
+
+  TelephonyIPCProvider();
+
+protected:
+  virtual ~TelephonyIPCProvider();
+
+private:
+  nsTArray<nsCOMPtr<nsITelephonyListener> > mListeners;
+  PTelephonyChild* mPTelephonyChild;
+};
+
+END_TELEPHONY_NAMESPACE
+
+#endif // mozilla_dom_telephony_TelephonyIPCProvider_h
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyParent.cpp
@@ -0,0 +1,448 @@
+/* -*- 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/TelephonyParent.h"
+
+USING_TELEPHONY_NAMESPACE
+
+/*******************************************************************************
+ * TelephonyParent
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS1(TelephonyParent, nsITelephonyListener)
+
+TelephonyParent::TelephonyParent()
+  : mActorDestroyed(false)
+  , mRegistered(false)
+{
+}
+
+void
+TelephonyParent::ActorDestroy(ActorDestroyReason why)
+{
+  // The child process could die before this asynchronous notification, in which
+  // case ActorDestroy() was called and mActorDestroyed is set to true. Return
+  // an error here to avoid sending a message to the dead process.
+  mActorDestroyed = true;
+
+  // Try to unregister listener if we're still registered.
+  RecvUnregisterListener();
+}
+
+bool
+TelephonyParent::RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor)
+{
+  TelephonyRequestParent* actor = static_cast<TelephonyRequestParent*>(aActor);
+
+  return actor->DoRequest();
+}
+
+PTelephonyRequestParent*
+TelephonyParent::AllocPTelephonyRequestParent()
+{
+  TelephonyRequestParent* actor = new TelephonyRequestParent();
+  // Add an extra ref for IPDL. Will be released in
+  // TelephonyParent::DeallocPTelephonyRequestParent().
+  NS_ADDREF(actor);
+
+  return actor;
+}
+
+bool
+TelephonyParent::DeallocPTelephonyRequestParent(PTelephonyRequestParent* aActor)
+{
+  // TelephonyRequestParent is refcounted, must not be freed manually.
+  static_cast<TelephonyRequestParent*>(aActor)->Release();
+  return true;
+}
+
+bool
+TelephonyParent::Recv__delete__()
+{
+  return true; // Unregister listener in TelephonyParent::ActorDestroy().
+}
+
+bool
+TelephonyParent::RecvRegisterListener()
+{
+  NS_ENSURE_TRUE(!mRegistered, true);
+
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  mRegistered = NS_SUCCEEDED(provider->RegisterListener(this));
+  return true;
+}
+
+bool
+TelephonyParent::RecvUnregisterListener()
+{
+  NS_ENSURE_TRUE(mRegistered, true);
+
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  mRegistered = !NS_SUCCEEDED(provider->UnregisterListener(this));
+  return true;
+}
+
+bool
+TelephonyParent::RecvDialCall(const nsString& aNumber,
+                              const bool& aIsEmergency)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->Dial(aNumber, aIsEmergency);
+  return true;
+}
+
+bool
+TelephonyParent::RecvHangUpCall(const uint32_t& aCallIndex)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->HangUp(aCallIndex);
+  return true;
+}
+
+bool
+TelephonyParent::RecvAnswerCall(const uint32_t& aCallIndex)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->AnswerCall(aCallIndex);
+  return true;
+}
+
+bool
+TelephonyParent::RecvRejectCall(const uint32_t& aCallIndex)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->RejectCall(aCallIndex);
+  return true;
+}
+
+bool
+TelephonyParent::RecvHoldCall(const uint32_t& aCallIndex)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->HoldCall(aCallIndex);
+  return true;
+}
+
+bool
+TelephonyParent::RecvResumeCall(const uint32_t& aCallIndex)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->ResumeCall(aCallIndex);
+  return true;
+}
+
+bool
+TelephonyParent::RecvConferenceCall()
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->ConferenceCall();
+  return true;
+}
+
+bool
+TelephonyParent::RecvSeparateCall(const uint32_t& aCallState)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->SeparateCall(aCallState);
+  return true;
+}
+
+bool
+TelephonyParent::RecvHoldConference()
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->HoldConference();
+  return true;
+}
+
+bool
+TelephonyParent::RecvResumeConference()
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->ResumeConference();
+  return true;
+}
+
+bool
+TelephonyParent::RecvStartTone(const nsString& aTone)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->StartTone(aTone);
+  return true;
+}
+
+bool
+TelephonyParent::RecvStopTone()
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->StopTone();
+  return true;
+}
+
+bool
+TelephonyParent::RecvGetMicrophoneMuted(bool* aMuted)
+{
+  *aMuted = false;
+
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->GetMicrophoneMuted(aMuted);
+  return true;
+}
+
+bool
+TelephonyParent::RecvSetMicrophoneMuted(const bool& aMuted)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->SetMicrophoneMuted(aMuted);
+  return true;
+}
+
+bool
+TelephonyParent::RecvGetSpeakerEnabled(bool* aEnabled)
+{
+  *aEnabled = false;
+
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->GetSpeakerEnabled(aEnabled);
+  return true;
+}
+
+bool
+TelephonyParent::RecvSetSpeakerEnabled(const bool& aEnabled)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  NS_ENSURE_TRUE(provider, true);
+
+  provider->SetSpeakerEnabled(aEnabled);
+  return true;
+}
+
+// nsITelephonyListener
+
+NS_IMETHODIMP
+TelephonyParent::CallStateChanged(uint32_t aCallIndex,
+                                  uint16_t aCallState,
+                                  const nsAString& aNumber,
+                                  bool aIsActive,
+                                  bool aIsOutgoing,
+                                  bool aIsEmergency,
+                                  bool aIsConference)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  IPCCallStateData data(aCallIndex, aCallState, nsString(aNumber), aIsActive,
+                        aIsOutgoing, aIsEmergency, aIsConference);
+  return SendNotifyCallStateChanged(data) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyParent::ConferenceCallStateChanged(uint16_t aCallState)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return SendNotifyConferenceCallStateChanged(aCallState) ? NS_OK
+                                                          : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyParent::EnumerateCallStateComplete()
+{
+  MOZ_CRASH("Not a EnumerateCalls request!");
+}
+
+NS_IMETHODIMP
+TelephonyParent::EnumerateCallState(uint32_t aCallIndex,
+                                    uint16_t aCallState,
+                                    const nsAString& aNumber,
+                                    bool aIsActive,
+                                    bool aIsOutgoing,
+                                    bool aIsEmergency,
+                                    bool aIsConference)
+{
+  MOZ_CRASH("Not a EnumerateCalls request!");
+}
+
+NS_IMETHODIMP
+TelephonyParent::NotifyCdmaCallWaiting(const nsAString& aNumber)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return SendNotifyCdmaCallWaiting(nsString(aNumber)) ? NS_OK
+                                                      : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyParent::NotifyError(int32_t aCallIndex,
+                             const nsAString& aError)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return SendNotifyCallError(aCallIndex, nsString(aError)) ? NS_OK
+                                                           : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyParent::SupplementaryServiceNotification(int32_t aCallIndex,
+                                                  uint16_t aNotification)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return SendNotifySupplementaryService(aCallIndex, aNotification)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/*******************************************************************************
+ * TelephonyRequestParent
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS1(TelephonyRequestParent, nsITelephonyListener)
+
+TelephonyRequestParent::TelephonyRequestParent()
+  : mActorDestroyed(false)
+{
+}
+
+void
+TelephonyRequestParent::ActorDestroy(ActorDestroyReason why)
+{
+  // The child process could die before this asynchronous notification, in which
+  // case ActorDestroy() was called and mActorDestroyed is set to true. Return
+  // an error here to avoid sending a message to the dead process.
+  mActorDestroyed = true;
+}
+
+bool
+TelephonyRequestParent::DoRequest()
+{
+  nsresult rv = NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  if (provider) {
+    rv = provider->EnumerateCalls(this);
+  }
+
+  if (NS_FAILED(rv)) {
+    return NS_SUCCEEDED(EnumerateCallStateComplete());
+  }
+
+  return true;
+}
+
+// nsITelephonyListener
+
+NS_IMETHODIMP
+TelephonyRequestParent::CallStateChanged(uint32_t aCallIndex,
+                                         uint16_t aCallState,
+                                         const nsAString& aNumber,
+                                         bool aIsActive,
+                                         bool aIsOutgoing,
+                                         bool aIsEmergency,
+                                         bool aIsConference)
+{
+  MOZ_CRASH("Not a TelephonyParent!");
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::ConferenceCallStateChanged(uint16_t aCallState)
+{
+  MOZ_CRASH("Not a TelephonyParent!");
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::EnumerateCallStateComplete()
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return Send__delete__(this) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::EnumerateCallState(uint32_t aCallIndex,
+                                           uint16_t aCallState,
+                                           const nsAString& aNumber,
+                                           bool aIsActive,
+                                           bool aIsOutgoing,
+                                           bool aIsEmergency,
+                                           bool aIsConference)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  IPCCallStateData data(aCallIndex, aCallState, nsString(aNumber), aIsActive,
+                        aIsOutgoing, aIsEmergency, aIsConference);
+  return SendNotifyEnumerateCallState(data) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::NotifyCdmaCallWaiting(const nsAString& aNumber)
+{
+  MOZ_CRASH("Not a TelephonyParent!");
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::NotifyError(int32_t aCallIndex,
+                                    const nsAString& aError)
+{
+  MOZ_CRASH("Not a TelephonyParent!");
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::SupplementaryServiceNotification(int32_t aCallIndex,
+                                                         uint16_t aNotification)
+{
+  MOZ_CRASH("Not a TelephonyParent!");
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyParent.h
@@ -0,0 +1,128 @@
+/* -*- 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_TelephonyParent_h
+#define mozilla_dom_telephony_TelephonyParent_h
+
+#include "mozilla/dom/telephony/TelephonyCommon.h"
+#include "mozilla/dom/telephony/PTelephonyParent.h"
+#include "mozilla/dom/telephony/PTelephonyRequestParent.h"
+#include "nsITelephonyProvider.h"
+
+BEGIN_TELEPHONY_NAMESPACE
+
+class TelephonyParent : public PTelephonyParent
+                      , public nsITelephonyListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITELEPHONYLISTENER
+
+  TelephonyParent();
+
+protected:
+  virtual ~TelephonyParent() {}
+
+  virtual void
+  ActorDestroy(ActorDestroyReason why);
+
+  virtual bool
+  RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor) MOZ_OVERRIDE;
+
+  virtual PTelephonyRequestParent*
+  AllocPTelephonyRequestParent() MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPTelephonyRequestParent(PTelephonyRequestParent* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  Recv__delete__() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvRegisterListener() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvUnregisterListener() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDialCall(const nsString& aNumber,
+               const bool& aIsEmergency) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvHangUpCall(const uint32_t& aCallIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvAnswerCall(const uint32_t& aCallIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvRejectCall(const uint32_t& aCallIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvHoldCall(const uint32_t& aCallIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvResumeCall(const uint32_t& aCallIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvConferenceCall() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvSeparateCall(const uint32_t& callIndex) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvHoldConference() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvResumeConference() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvStartTone(const nsString& aTone) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvStopTone() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvGetMicrophoneMuted(bool* aMuted) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvSetMicrophoneMuted(const bool& aMuted) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvGetSpeakerEnabled(bool* aEnabled) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvSetSpeakerEnabled(const bool& aEnabled) MOZ_OVERRIDE;
+
+private:
+  bool mActorDestroyed;
+  bool mRegistered;
+};
+
+class TelephonyRequestParent : public PTelephonyRequestParent
+                             , public nsITelephonyListener
+{
+  friend class TelephonyParent;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITELEPHONYLISTENER
+
+protected:
+  TelephonyRequestParent();
+  virtual ~TelephonyRequestParent() {}
+
+  virtual void
+  ActorDestroy(ActorDestroyReason why);
+
+private:
+  bool mActorDestroyed;
+
+  bool
+  DoRequest();
+};
+
+END_TELEPHONY_NAMESPACE
+
+#endif /* mozilla_dom_telephony_TelephonyParent_h */
new file mode 100644
--- /dev/null
+++ b/dom/telephony/ipc/TelephonyTypes.ipdlh
@@ -0,0 +1,24 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+namespace telephony {
+
+struct IPCCallStateData
+{
+  uint32_t callIndex;
+  uint16_t callState;
+  nsString number;
+  bool     isActive;
+  bool     isOutGoing;
+  bool     isEmergency;
+  bool     isConference;
+};
+
+} /* namespace telephony */
+} /* namespace dom */
+} /* namespace mozilla */
--- a/dom/telephony/moz.build
+++ b/dom/telephony/moz.build
@@ -7,22 +7,52 @@
 XPIDL_SOURCES += [
     'nsITelephonyProvider.idl',
 ]
 
 XPIDL_MODULE = 'dom_telephony'
 
 MODULE = 'dom'
 
+EXPORTS.mozilla.dom.telephony += [
+    'CallEvent.h',
+    'CallsList.h',
+    'Telephony.h',
+    'TelephonyCall.h',
+    'TelephonyCallGroup.h',
+    'TelephonyCommon.h',
+    'TelephonyFactory.h',
+    'ipc/TelephonyChild.h',
+    'ipc/TelephonyParent.h',
+]
+
 CPP_SOURCES += [
     'CallEvent.cpp',
     'CallsList.cpp',
     'Telephony.cpp',
     'TelephonyCall.cpp',
     'TelephonyCallGroup.cpp',
+    'TelephonyFactory.cpp',
+    'ipc/TelephonyChild.cpp',
+    'ipc/TelephonyIPCProvider.cpp',
+    'ipc/TelephonyParent.cpp',
 ]
 
+IPDL_SOURCES += [
+    'ipc/PTelephony.ipdl',
+    'ipc/PTelephonyRequest.ipdl',
+    'ipc/TelephonyTypes.ipdlh'
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    XPIDL_SOURCES += [
+        'nsIGonkTelephonyProvider.idl',
+    ]
+    EXTRA_COMPONENTS += [
+        'gonk/TelephonyProvider.js',
+        'gonk/TelephonyProvider.manifest',
+    ]
+
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'domtelephony_s'
-
new file mode 100644
--- /dev/null
+++ b/dom/telephony/nsIGonkTelephonyProvider.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: idl; 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 "nsITelephonyProvider.idl"
+
+%{C++
+#define GONK_TELEPHONY_PROVIDER_CONTRACTID \
+        "@mozilla.org/telephony/gonktelephonyprovider;1"
+%}
+
+[scriptable, uuid(0d106c7e-ba47-48ee-ba48-c92002d401b6)]
+interface nsIGonkTelephonyProvider : nsITelephonyProvider
+{
+  void notifyCallDisconnected(in jsval call);
+
+  void notifyCallError(in long callIndex,
+                       in AString error);
+
+  void notifyCallRing();
+
+  void notifyCallStateChanged(in jsval call);
+
+  void notifyCdmaCallWaiting(in AString number);
+
+  void notifySupplementaryService(in long callIndex,
+                                  in AString notification);
+
+  void notifyConferenceCallStateChanged(in unsigned short state);
+};
--- a/dom/telephony/nsITelephonyProvider.idl
+++ b/dom/telephony/nsITelephonyProvider.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 "nsISupports.idl"
 
-[scriptable, uuid(a5818719-e1b6-4fdc-8551-006055fa9996)]
+[scriptable, uuid(3aa42e77-7c2b-43a1-b105-7be094b0817a)]
 interface nsITelephonyListener : nsISupports
 {
   /**
    * Notified when a telephony call changes state.
    *
    * @param callIndex
    *        Call identifier assigned by the RIL.
    * @param callState
@@ -62,25 +62,24 @@ interface nsITelephonyListener : nsISupp
    * @param number
    *        Number of the other party.
    * @param isActive
    *        Indicates whether this call is the active one.
    * @param isOutgoing
    *        Indicates whether this call is outgoing or incoming.
    * @param isConference
    *        Indicates whether this call is a conference call.
-   * @return true to continue enumeration or false to cancel.
    */
-  boolean enumerateCallState(in unsigned long callIndex,
-                             in unsigned short callState,
-                             in AString number,
-                             in boolean isActive,
-                             in boolean isOutgoing,
-                             in boolean isEmergency,
-                             in boolean isConference);
+  void enumerateCallState(in unsigned long callIndex,
+                          in unsigned short callState,
+                          in AString number,
+                          in boolean isActive,
+                          in boolean isOutgoing,
+                          in boolean isEmergency,
+                          in boolean isConference);
 
   /**
    * Notify when RIL receives supplementary service notification.
    *
    * @param callIndex
    *        Call identifier assigned by the RIL. -1 if not specified
    * @param notification
    *        One of the nsITelephonyProvider::NOTIFICATION_* values.
@@ -103,21 +102,28 @@ interface nsITelephonyListener : nsISupp
    * Called when a waiting call comes in CDMA networks.
    *
    * @param number
    *        Number of the other party.
    */
   void notifyCdmaCallWaiting(in AString number);
 };
 
+%{C++
+#define TELEPHONY_PROVIDER_CID \
+  { 0x9cf8aa52, 0x7c1c, 0x4cde, { 0x97, 0x4e, 0xed, 0x2a, 0xa0, 0xe7, 0x35, 0xfa } }
+#define TELEPHONY_PROVIDER_CONTRACTID \
+  "@mozilla.org/telephony/telephonyprovider;1"
+%}
+
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(45a2f856-4e07-499a-94c6-624f90c3345b)]
+[scriptable, uuid(effca006-1ca8-47f7-9bab-1323f90a14ec)]
 interface nsITelephonyProvider : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_CONNECTING = 3;
   const unsigned short CALL_STATE_CONNECTED = 4;
   const unsigned short CALL_STATE_HOLDING = 5;
@@ -130,30 +136,30 @@ interface nsITelephonyProvider : nsISupp
   const unsigned short NOTIFICATION_REMOTE_HELD = 0;
   const unsigned short NOTIFICATION_REMOTE_RESUMED = 1;
 
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'telephony' permission is allowed to register.
    */
-  void registerTelephonyMsg(in nsITelephonyListener listener);
-  void unregisterTelephonyMsg(in nsITelephonyListener listener);
+  void registerListener(in nsITelephonyListener listener);
+  void unregisterListener(in nsITelephonyListener listener);
 
   /**
    * Will continue calling listener.enumerateCallState until the listener
    * returns false.
    */
   void enumerateCalls(in nsITelephonyListener listener);
 
   /**
    * Functionality for making and managing phone calls.
    */
-  void dial(in DOMString number);
-  void dialEmergency(in DOMString number);
+  void dial(in DOMString number,
+            in boolean isEmergency);
   void hangUp(in unsigned long callIndex);
 
   void startTone(in DOMString dtmfChar);
   void stopTone();
 
   void answerCall(in unsigned long callIndex);
   void rejectCall(in unsigned long callIndex);
   void holdCall(in unsigned long callIndex);
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -97,17 +97,17 @@ var interfaceNamesInGlobalScope =
     "BlobEvent",
     {name: "BluetoothAdapter", b2g: true},
     {name: "BluetoothDevice", b2g: true},
     {name: "BluetoothDeviceEvent", b2g: true},
     {name: "BluetoothManager", b2g: true},
     {name: "BluetoothStatusChangedEvent", b2g: true},
     {name: "BoxObject", xbl: true},
     {name: "BrowserFeedWriter", desktop: true},
-    {name: "CallEvent", b2g: true},
+    "CallEvent",
     "CameraCapabilities",
     "CameraControl",
     "CameraManager",
     "CanvasGradient",
     "CanvasPattern",
     "CanvasRenderingContext2D",
     "CaretPosition",
     "CDATASection",
@@ -526,19 +526,19 @@ var interfaceNamesInGlobalScope =
     "SVGTransform",
     "SVGTransformList",
     "SVGTSpanElement",
     "SVGUnitTypes",
     "SVGUseElement",
     "SVGViewElement",
     "SVGZoomAndPan",
     "SVGZoomEvent",
-    {name: "Telephony", b2g: true},
-    {name: "TelephonyCall", b2g: true},
-    {name: "TelephonyCallGroup", b2g: true},
+    "Telephony",
+    "TelephonyCall",
+    "TelephonyCallGroup",
     "Text",
     "TextDecoder",
     "TextEncoder",
     "TextMetrics",
     "TimeEvent",
     "TimeRanges",
     "Touch",
     "TouchEvent",
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -29,16 +29,18 @@ webidl_files = \
   AudioProcessingEvent.webidl \
   AudioStreamTrack.webidl \
   BarProp.webidl \
   BatteryManager.webidl \
   BeforeUnloadEvent.webidl \
   BiquadFilterNode.webidl \
   Blob.webidl \
   BrowserElementDictionaries.webidl \
+  CallEvent.webidl \
+  CallsList.webidl \
   CameraControl.webidl \
   CameraManager.webidl \
   CanvasRenderingContext2D.webidl \
   CaretPosition.webidl \
   CDATASection.webidl \
   ChannelMergerNode.webidl \
   ChannelSplitterNode.webidl \
   CharacterData.webidl \
@@ -354,16 +356,19 @@ webidl_files = \
   SVGTransformList.webidl \
   SVGTSpanElement.webidl \
   SVGUnitTypes.webidl \
   SVGURIReference.webidl \
   SVGUseElement.webidl \
   SVGViewElement.webidl \
   SVGZoomAndPan.webidl \
   SVGZoomEvent.webidl \
+  Telephony.webidl \
+  TelephonyCall.webidl \
+  TelephonyCallGroup.webidl \
   Text.webidl \
   TextDecoder.webidl \
   TextEncoder.webidl \
   TextTrack.webidl \
   TextTrackCueList.webidl \
   TextTrackList.webidl \
   TimeEvent.webidl \
   TimeRanges.webidl \
@@ -440,22 +445,18 @@ endif
 ifdef MOZ_GAMEPAD
 webidl_files += \
   Gamepad.webidl \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_RIL
 webidl_files += \
-  CallsList.webidl \
   MozStkCommandEvent.webidl \
   MozVoicemail.webidl \
-  Telephony.webidl \
-  TelephonyCall.webidl \
-  TelephonyCallGroup.webidl \
   $(NULL)
 endif
 
 webidl_files += \
   BlobEvent.webidl \
   CloseEvent.webidl \
   CustomEvent.webidl \
   DeviceLightEvent.webidl \
@@ -490,17 +491,16 @@ webidl_files += \
   BluetoothDeviceEvent.webidl \
   BluetoothManager.webidl \
   BluetoothStatusChangedEvent.webidl \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_RIL
 webidl_files += \
-  CallEvent.webidl \
   CFStateChangeEvent.webidl \
   DataErrorEvent.webidl \
   IccCardLockErrorEvent.webidl \
   MozCellBroadcast.webidl \
   MozCellBroadcastEvent.webidl \
   MozEmergencyCbModeEvent.webidl \
   MozOtaStatusEvent.webidl \
   MozVoicemailEvent.webidl \
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -54,16 +54,17 @@ SHARED_LIBRARY_LIBS = \
 	$(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/geolocation/$(LIB_PREFIX)jsdomgeolocation_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/audiochannel/$(LIB_PREFIX)domaudiochannel_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/promise/$(LIB_PREFIX)dompromise_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/notification/$(LIB_PREFIX)jsdomnotification_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/system/$(LIB_PREFIX)domsystem_s.$(LIB_SUFFIX) \
+	$(DEPTH)/dom/telephony/$(LIB_PREFIX)domtelephony_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/workers/$(LIB_PREFIX)domworkers_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/$(LIB_PREFIX)dom_indexeddb_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/ipc/$(LIB_PREFIX)dom_indexeddb_ipc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/browser-element/$(LIB_PREFIX)dom_browserelement_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/time/$(LIB_PREFIX)dom_time_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/text/$(LIB_PREFIX)texteditor_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/base/$(LIB_PREFIX)editorbase_s.$(LIB_SUFFIX) \
 	$(DEPTH)/parser/html/$(LIB_PREFIX)html5p_s.$(LIB_SUFFIX) \
@@ -124,17 +125,16 @@ ifdef MOZ_B2G_BT #{
 SHARED_LIBRARY_LIBS += $(DEPTH)/dom/bluetooth/$(LIB_PREFIX)dombluetooth_s.$(LIB_SUFFIX)
 endif #}
 
 SHARED_LIBRARY_LIBS	+= $(DEPTH)/dom/camera/$(LIB_PREFIX)domcamera_s.$(LIB_SUFFIX)
 
 ifdef MOZ_B2G_RIL #{
 SHARED_LIBRARY_LIBS	+= \
   $(DEPTH)/dom/system/gonk/$(LIB_PREFIX)domsystemgonk_s.$(LIB_SUFFIX) \
-  $(DEPTH)/dom/telephony/$(LIB_PREFIX)domtelephony_s.$(LIB_SUFFIX) \
   $(DEPTH)/dom/icc/src/$(LIB_PREFIX)dom_icc_s.$(LIB_SUFFIX) \
   $(DEPTH)/dom/cellbroadcast/src/$(LIB_PREFIX)dom_cellbroadcast_s.$(LIB_SUFFIX) \
   $(DEPTH)/dom/voicemail/$(LIB_PREFIX)domvoicemail_s.$(LIB_SUFFIX) \
   $(NULL)
 endif #}
 
 ifdef MOZ_OGG
 SHARED_LIBRARY_LIBS 	+= \
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -225,24 +225,28 @@ static void Shutdown();
 #include "nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
 
+#include "mozilla/dom/telephony/TelephonyFactory.h"
+#include "nsITelephonyProvider.h"
+
 #ifdef MOZ_WIDGET_GONK
 #include "GonkGPSGeolocationProvider.h"
 #endif
 #include "MediaManager.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::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::TCPSocketParent;
 using mozilla::dom::TCPServerSocketChild;
 using mozilla::dom::time::TimeService;
@@ -333,16 +337,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(nsITelephonyProvider,
+                                         TelephonyFactory::CreateTelephonyProvider)
 
 //-----------------------------------------------------------------------------
 
 // Per bug 209804, it is necessary to observe the "xpcom-shutdown" event and
 // perform shutdown of the layout modules at that time instead of waiting for
 // our module destructor to run.  If we do not do this, then we risk holding
 // references to objects in other component libraries that have already been
 // shutdown (and possibly unloaded if 60709 is ever fixed).
@@ -806,16 +812,17 @@ NS_DEFINE_NAMED_CID(NS_GAMEPAD_TEST_CID)
 #ifdef MOZ_WEBSPEECH
 NS_DEFINE_NAMED_CID(NS_FAKE_SPEECH_RECOGNITION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID);
 #endif
 
 #ifdef ACCESSIBILITY
 NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID);
 #endif
+NS_DEFINE_NAMED_CID(TELEPHONY_PROVIDER_CID);
 
 static nsresult
 CreateWindowCommandTableConstructor(nsISupports *aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIControllerCommandTable> commandTable =
       do_CreateInstance(NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &rv);
@@ -1088,16 +1095,17 @@ static const mozilla::Module::CIDEntry k
 #endif
   { &kNS_MEDIAMANAGERSERVICE_CID, false, NULL, nsIMediaManagerServiceConstructor },
 #ifdef MOZ_GAMEPAD
   { &kNS_GAMEPAD_TEST_CID, false, NULL, GamepadServiceTestConstructor },
 #endif
 #ifdef ACCESSIBILITY
   { &kNS_ACCESSIBILITY_SERVICE_CID, false, NULL, CreateA11yService },
 #endif
+  { &kTELEPHONY_PROVIDER_CID, false, NULL, nsITelephonyProviderConstructor },
   { NULL }
 };
 
 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 },
@@ -1243,16 +1251,17 @@ static const mozilla::Module::ContractID
 #ifdef MOZ_GAMEPAD
   { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
 #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_PROVIDER_CONTRACTID, &kTELEPHONY_PROVIDER_CID },
   { NULL }
 };
 
 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 },
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -267,17 +267,17 @@ this.OnRefTestLoad = function OnRefTestL
     gBrowser.setAttribute("type", "content-primary");
     gBrowser.setAttribute("remote", gBrowserIsRemote ? "true" : "false");
     // Make sure the browser element is exactly 800x1000, no matter
     // what size our window is
     gBrowser.setAttribute("style", "min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px");
 
 #if BOOTSTRAP
 #if REFTEST_B2G
-    var doc = gContainingWindow.document.getElementsByTagName("window")[0];
+    var doc = gContainingWindow.document.getElementsByTagName("html")[0];
 #else
     var doc = gContainingWindow.document.getElementById('main-window');
 #endif
     while (doc.hasChildNodes()) {
       doc.removeChild(doc.firstChild);
     }
     doc.appendChild(gBrowser);
 #else
--- a/testing/marionette/client/marionette/tests/unit/test_element_touch.py
+++ b/testing/marionette/client/marionette/tests/unit/test_element_touch.py
@@ -8,17 +8,18 @@ from errors import MarionetteException
 class testElementTouch(MarionetteTestCase):
     def test_touch(self):
       testAction = self.marionette.absolute_url("testAction.html")
       self.marionette.navigate(testAction)
       button = self.marionette.find_element("id", "button1")
       button.tap()
       expected = "button1-touchstart-touchend-mousemove-mousedown-mouseup-click"
       self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
-      button.tap(0, 300)
+      button = self.marionette.find_element("id", "button2")
+      button.tap()
       expected = "button2-touchstart-touchend-mousemove-mousedown-mouseup-click"
       self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button2').innerHTML;") == expected)
 
     def test_invisible(self):
       testAction = self.marionette.absolute_url("testAction.html")
       self.marionette.navigate(testAction)
       ele = self.marionette.find_element("id", "hidden")
       self.assertRaises(MarionetteException, ele.tap)
--- a/testing/mochitest/b2g_start_script.js
+++ b/testing/mochitest/b2g_start_script.js
@@ -4,17 +4,17 @@
 
 let outOfProcess = __marionetteParams[0]
 let mochitestUrl = __marionetteParams[1]
 
 const CHILD_SCRIPT = "chrome://specialpowers/content/specialpowers.js";
 const CHILD_SCRIPT_API = "chrome://specialpowers/content/specialpowersAPI.js";
 const CHILD_LOGGER_SCRIPT = "chrome://specialpowers/content/MozillaLogger.js";
 
-let homescreen = document.getElementById('homescreen');
+let homescreen = document.getElementById('systemapp');
 let container = homescreen.contentWindow.document.getElementById('test-container');
 
 function openWindow(aEvent) {
   var popupIframe = aEvent.detail.frameElement;
   popupIframe.setAttribute('style', 'position: absolute; left: 0; top: 300px; background: white; ');
 
   popupIframe.addEventListener('mozbrowserclose', function(e) {
     container.parentNode.removeChild(popupIframe);
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -404,17 +404,17 @@ toolbar#nav-bar {
       return None
 
     # Support Firefox (browser), B2G (shell), SeaMonkey (navigator), and Webapp
     # Runtime (webapp).
     chrome = ""
     if options.browserChrome or options.chrome or options.a11y or options.webapprtChrome:
       chrome += """
 overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul
-overlay chrome://browser/content/shell.xul chrome://mochikit/content/browser-test-overlay.xul
+overlay chrome://browser/content/shell.xhtml chrome://mochikit/content/browser-test-overlay.xul
 overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul
 overlay chrome://webapprt/content/webapp.xul chrome://mochikit/content/browser-test-overlay.xul
 """
 
     self.installChromeJar(chrome, options)
     return manifest
 
 
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -173,16 +173,29 @@ var DebuggerServer = {
   /**
    * The windowtype of the chrome window to use for actors that use the global
    * window (i.e the global style editor). Set this to your main window type,
    * for example "navigator:browser".
    */
   chromeWindowType: null,
 
   /**
+   * Set that to a function that will be called anytime a new connection
+   * is opened or one is closed.
+   */
+  onConnectionChange: null,
+
+  _fireConnectionChange: function(aWhat) {
+    if (this.onConnectionChange &&
+        typeof this.onConnectionChange === "function") {
+      this.onConnectionChange(aWhat);
+    }
+  },
+
+  /**
    * Prompt the user to accept or decline the incoming connection. This is the
    * default implementation that products embedding the debugger server may
    * choose to override.
    *
    * @return true if the connection should be permitted, false otherwise
    */
   _defaultAllowConnection: function DS__defaultAllowConnection() {
     let title = L10N.getStr("remoteIncomingPromptTitle");
@@ -270,16 +283,19 @@ var DebuggerServer = {
     gRegisteredModules = {};
 
     this.closeListener();
     this.globalActorFactories = {};
     this.tabActorFactories = {};
     delete this._allowConnection;
     this._transportInitialized = false;
     this._initialized = false;
+
+    this._fireConnectionChange("closed");
+
     dumpn("Debugger server is shut down.");
   },
 
   /**
    * Load a subscript into the debugging global.
    *
    * @param aURL string A url that will be loaded as a subscript into the
    *        debugging global.  The user must load at least one script
@@ -541,24 +557,26 @@ var DebuggerServer = {
         conn.rootActor.actorID = aForwardingPrefix + ":root";
       else
         conn.rootActor.actorID = "root";
       conn.addActor(conn.rootActor);
       aTransport.send(conn.rootActor.sayHello());
     }
     aTransport.ready();
 
+    this._fireConnectionChange("opened");
     return conn;
   },
 
   /**
    * Remove the connection from the debugging server.
    */
   _connectionClosed: function DS_connectionClosed(aConnection) {
     delete this._connections[aConnection.prefix];
+    this._fireConnectionChange("closed");
   },
 
   // DebuggerServer extension API.
 
   /**
    * Registers handlers for new tab-scoped request types defined dynamically.
    * This is used for example by add-ons to augment the functionality of the tab
    * actor. Note that the name or actorPrefix of the request type is not allowed