Merge b2g-inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 03 Sep 2014 14:53:26 -0400
changeset 203209 117271830c4d201543d85e835194fe89253282f3
parent 203127 5e9826980be5999a66f6c3f8c9d2a95762fa6788 (current diff)
parent 203208 e8fd97e5279e037a68c192be6b5a54c6bb45fe45 (diff)
child 203256 bfef88becbba6972b2a5c1f92afac2ab4a9f0d4c
push id27423
push userryanvm@gmail.com
push dateWed, 03 Sep 2014 18:53:23 +0000
treeherdermozilla-central@117271830c4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge b2g-inbound to m-c. a=merge
dom/mobileconnection/interfaces/nsIMobileConnectionProvider.idl
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -189,16 +189,34 @@ SettingsListener.observe('devtools.overl
     developerHUD.init();
   } else {
     if (developerHUD) {
       developerHUD.uninit();
     }
   }
 });
 
+#ifdef MOZ_WIDGET_GONK
+let LogShake;
+SettingsListener.observe('devtools.logshake', false, (value) => {
+  if (value) {
+    if (!LogShake) {
+      let scope = {};
+      Cu.import('resource://gre/modules/LogShake.jsm', scope);
+      LogShake = scope.LogShake;
+    }
+    LogShake.init();
+  } else {
+    if (LogShake) {
+      LogShake.uninit();
+    }
+  }
+});
+#endif
+
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
     // and we need to clear it before we can change it to have the correct type.
     Services.prefs.clearUserPref('device.storage.writable.name');
   }
   Services.prefs.setCharPref('device.storage.writable.name', value);
new file mode 100644
--- /dev/null
+++ b/b2g/components/LogCapture.jsm
@@ -0,0 +1,91 @@
+/* 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/. */
+/* jshint moz: true */
+/* global Uint8Array, Components, dump */
+
+'use strict';
+
+this.EXPORTED_SYMBOLS = ['LogCapture'];
+
+/**
+ * readLogFile
+ * Read in /dev/log/{{log}} in nonblocking mode, which will return -1 if
+ * reading would block the thread.
+ *
+ * @param log {String} The log from which to read. Must be present in /dev/log
+ * @return {Uint8Array} Raw log data
+ */
+let readLogFile = function(logLocation) {
+  if (!this.ctypes) {
+    // load in everything on first use
+    Components.utils.import('resource://gre/modules/ctypes.jsm', this);
+
+    this.lib = this.ctypes.open(this.ctypes.libraryName('c'));
+
+    this.read = this.lib.declare('read',
+      this.ctypes.default_abi,
+      this.ctypes.int,       // bytes read (out)
+      this.ctypes.int,       // file descriptor (in)
+      this.ctypes.voidptr_t, // buffer to read into (in)
+      this.ctypes.size_t     // size_t size of buffer (in)
+    );
+
+    this.open = this.lib.declare('open',
+      this.ctypes.default_abi,
+      this.ctypes.int,      // file descriptor (returned)
+      this.ctypes.char.ptr, // path
+      this.ctypes.int       // flags
+    );
+
+    this.close = this.lib.declare('close',
+      this.ctypes.default_abi,
+      this.ctypes.int, // error code (returned)
+      this.ctypes.int  // file descriptor
+    );
+  }
+
+  const O_READONLY = 0;
+  const O_NONBLOCK = 1 << 11;
+
+  const BUF_SIZE = 2048;
+
+  let BufType = this.ctypes.ArrayType(this.ctypes.char);
+  let buf = new BufType(BUF_SIZE);
+  let logArray = [];
+
+  let logFd = this.open(logLocation, O_READONLY | O_NONBLOCK);
+  if (logFd === -1) {
+    return null;
+  }
+
+  let readStart = Date.now();
+  let readCount = 0;
+  while (true) {
+    let count = this.read(logFd, buf, BUF_SIZE);
+    readCount += 1;
+
+    if (count <= 0) {
+      // log has return due to being nonblocking or running out of things
+      break;
+    }
+    for(let i = 0; i < count; i++) {
+      logArray.push(buf[i]);
+    }
+  }
+
+  let logTypedArray = new Uint8Array(logArray);
+
+  this.close(logFd);
+
+  return logTypedArray;
+};
+
+let cleanup = function() {
+  this.lib.close();
+  this.read = this.open = this.close = null;
+  this.lib = null;
+  this.ctypes = null;
+};
+
+this.LogCapture = { readLogFile: readLogFile, cleanup: cleanup };
new file mode 100644
--- /dev/null
+++ b/b2g/components/LogParser.jsm
@@ -0,0 +1,301 @@
+/* jshint esnext: true */
+/* global DataView */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["LogParser"];
+
+/**
+ * Parse an array read from a /dev/log/ file. Format taken from
+ * kernel/drivers/staging/android/logger.h and system/core/logcat/logcat.cpp
+ *
+ * @param array {Uint8Array} Array read from /dev/log/ file
+ * @return {Array} List of log messages
+ */
+function parseLogArray(array) {
+  let data = new DataView(array.buffer);
+  let byteString = String.fromCharCode.apply(null, array);
+
+  let logMessages = [];
+  let pos = 0;
+
+  while (pos < byteString.length) {
+    // Parse a single log entry
+
+    // Track current offset from global position
+    let offset = 0;
+
+    // Length of the entry, discarded
+    let length = data.getUint32(pos + offset, true);
+    offset += 4;
+    // Id of the process which generated the message
+    let processId = data.getUint32(pos + offset, true);
+    offset += 4;
+    // Id of the thread which generated the message
+    let threadId = data.getUint32(pos + offset, true);
+    offset += 4;
+    // Seconds since epoch when this message was logged
+    let seconds = data.getUint32(pos + offset, true);
+    offset += 4;
+    // Nanoseconds since the last second
+    let nanoseconds = data.getUint32(pos + offset, true);
+    offset += 4;
+
+    // Priority in terms of the ANDROID_LOG_* constants (see below)
+    // This is where the length field begins counting
+    let priority = data.getUint8(pos + offset);
+
+    // Reset pos and offset to count from here
+    pos += offset;
+    offset = 0;
+    offset += 1;
+
+    // Read the tag and message, represented as null-terminated c-style strings
+    let tag = "";
+    while (byteString[pos + offset] != "\0") {
+      tag += byteString[pos + offset];
+      offset ++;
+    }
+    offset ++;
+
+    let message = "";
+    // The kernel log driver may have cut off the null byte (logprint.c)
+    while (byteString[pos + offset] != "\0" && offset < length) {
+      message += byteString[pos + offset];
+      offset ++;
+    }
+
+    // Un-skip the missing null terminator
+    if (offset === length) {
+      offset --;
+    }
+
+    offset ++;
+
+    pos += offset;
+
+    // Log messages are occasionally delimited by newlines, but are also
+    // sometimes followed by newlines as well
+    if (message.charAt(message.length - 1) === "\n") {
+      message = message.substring(0, message.length - 1);
+    }
+
+    // Add an aditional time property to mimic the milliseconds since UTC
+    // expected by Date
+    let time = seconds * 1000.0 + nanoseconds/1000000.0;
+
+    // Log messages with interleaved newlines are considered to be separate log
+    // messages by logcat
+    for (let lineMessage of message.split("\n")) {
+      logMessages.push({
+        processId: processId,
+        threadId: threadId,
+        seconds: seconds,
+        nanoseconds: nanoseconds,
+        time: time,
+        priority: priority,
+        tag: tag,
+        message: lineMessage + "\n"
+      });
+    }
+  }
+
+  return logMessages;
+}
+
+/**
+ * Get a thread-time style formatted string from time
+ * @param time {Number} Milliseconds since epoch
+ * @return {String} Formatted time string
+ */
+function getTimeString(time) {
+  let date = new Date(time);
+  function pad(number) {
+    if ( number < 10 ) {
+      return "0" + number;
+    }
+    return number;
+  }
+  return pad( date.getMonth() + 1 ) +
+         "-" + pad( date.getDate() ) +
+         " " + pad( date.getHours() ) +
+         ":" + pad( date.getMinutes() ) +
+         ":" + pad( date.getSeconds() ) +
+         "." + (date.getMilliseconds() / 1000).toFixed(3).slice(2, 5);
+}
+
+/**
+ * Pad a string using spaces on the left
+ * @param str   {String} String to pad
+ * @param width {Number} Desired string length
+ */
+function padLeft(str, width) {
+  while (str.length < width) {
+    str = " " + str;
+  }
+  return str;
+}
+
+/**
+ * Pad a string using spaces on the right
+ * @param str   {String} String to pad
+ * @param width {Number} Desired string length
+ */
+function padRight(str, width) {
+  while (str.length < width) {
+    str = str + " ";
+  }
+  return str;
+}
+
+/** Constant values taken from system/core/liblog */
+const ANDROID_LOG_UNKNOWN = 0;
+const ANDROID_LOG_DEFAULT = 1;
+const ANDROID_LOG_VERBOSE = 2;
+const ANDROID_LOG_DEBUG   = 3;
+const ANDROID_LOG_INFO    = 4;
+const ANDROID_LOG_WARN    = 5;
+const ANDROID_LOG_ERROR   = 6;
+const ANDROID_LOG_FATAL   = 7;
+const ANDROID_LOG_SILENT  = 8;
+
+/**
+ * Map a priority number to its abbreviated string equivalent
+ * @param priorityNumber {Number} Log-provided priority number
+ * @return {String} Priority number's abbreviation
+ */
+function getPriorityString(priorityNumber) {
+  switch (priorityNumber) {
+  case ANDROID_LOG_VERBOSE:
+    return "V";
+  case ANDROID_LOG_DEBUG:
+    return "D";
+  case ANDROID_LOG_INFO:
+    return "I";
+  case ANDROID_LOG_WARN:
+    return "W";
+  case ANDROID_LOG_ERROR:
+    return "E";
+  case ANDROID_LOG_FATAL:
+    return "F";
+  case ANDROID_LOG_SILENT:
+    return "S";
+  default:
+    return "?";
+  }
+}
+
+
+/**
+ * Mimic the logcat "threadtime" format, generating a formatted string from a
+ * log message object.
+ * @param logMessage {Object} A log message from the list returned by parseLogArray
+ * @return {String} threadtime formatted summary of the message
+ */
+function formatLogMessage(logMessage) {
+  // MM-DD HH:MM:SS.ms pid tid priority tag: message
+  // from system/core/liblog/logprint.c:
+  return getTimeString(logMessage.time) +
+         " " + padLeft(""+logMessage.processId, 5) +
+         " " + padLeft(""+logMessage.threadId, 5) +
+         " " + getPriorityString(logMessage.priority) +
+         " " + padRight(logMessage.tag, 8) +
+         ": " + logMessage.message;
+}
+
+/**
+ * Pretty-print an array of bytes read from a log file by parsing then
+ * threadtime formatting its entries.
+ * @param array {Uint8Array} Array of a log file's bytes
+ * @return {String} Pretty-printed log
+ */
+function prettyPrintLogArray(array) {
+  let logMessages = parseLogArray(array);
+  return logMessages.map(formatLogMessage).join("");
+}
+
+/**
+ * Parse an array of bytes as a properties file. The structure of the
+ * properties file is derived from bionic/libc/bionic/system_properties.c
+ * @param array {Uint8Array} Array containing property data
+ * @return {Object} Map from property name to property value, both strings
+ */
+function parsePropertiesArray(array) {
+  let data = new DataView(array.buffer);
+  let byteString = String.fromCharCode.apply(null, array);
+
+  let properties = {};
+
+  let propIndex = 0;
+  let propCount = data.getUint32(0, true);
+
+  // first TOC entry is at 32
+  let tocOffset = 32;
+
+  const PROP_NAME_MAX = 32;
+  const PROP_VALUE_MAX = 92;
+
+  while (propIndex < propCount) {
+    // Retrieve offset from file start
+    let infoOffset = data.getUint32(tocOffset, true) & 0xffffff;
+
+    // Now read the name, integer serial, and value
+    let propName = "";
+    let nameOffset = infoOffset;
+    while (byteString[nameOffset] != "\0" &&
+           (nameOffset - infoOffset) < PROP_NAME_MAX) {
+      propName += byteString[nameOffset];
+      nameOffset ++;
+    }
+
+    infoOffset += PROP_NAME_MAX;
+    // Skip serial number
+    infoOffset += 4;
+
+    let propValue = "";
+    nameOffset = infoOffset;
+    while (byteString[nameOffset] != "\0" &&
+           (nameOffset - infoOffset) < PROP_VALUE_MAX) {
+      propValue += byteString[nameOffset];
+      nameOffset ++;
+    }
+
+    // Move to next table of contents entry
+    tocOffset += 4;
+
+    properties[propName] = propValue;
+    propIndex += 1;
+  }
+
+  return properties;
+}
+
+/**
+ * Pretty-print an array read from the /dev/__properties__ file.
+ * @param array {Uint8Array} File data array
+ * @return {String} Human-readable string of property name: property value
+ */
+function prettyPrintPropertiesArray(array) {
+  let properties = parsePropertiesArray(array);
+  let propertiesString = "";
+  for(let propName in properties) {
+    propertiesString += propName + ": " + properties[propName] + "\n";
+  }
+  return propertiesString;
+}
+
+/**
+ * Pretty-print a normal array. Does nothing.
+ * @param array {Uint8Array} Input array
+ */
+function prettyPrintArray(array) {
+  return array;
+}
+
+this.LogParser = {
+  parseLogArray: parseLogArray,
+  parsePropertiesArray: parsePropertiesArray,
+  prettyPrintArray: prettyPrintArray,
+  prettyPrintLogArray: prettyPrintLogArray,
+  prettyPrintPropertiesArray: prettyPrintPropertiesArray
+};
new file mode 100644
--- /dev/null
+++ b/b2g/components/LogShake.jsm
@@ -0,0 +1,301 @@
+/* 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/. */
+
+/**
+ * LogShake is a module which listens for log requests sent by Gaia. In
+ * response to a sufficiently large acceleration (a shake), it will save log
+ * files to an arbitrary directory which it will then return on a
+ * 'capture-logs-success' event with detail.logFilenames representing each log
+ * file's filename in the directory. If an error occurs it will instead produce
+ * a 'capture-logs-error' event.
+ */
+
+/* enable Mozilla javascript extensions and global strictness declaration,
+ * disable valid this checking */
+/* jshint moz: true */
+/* jshint -W097 */
+/* jshint -W040 */
+/* global Services, Components, dump, LogCapture, LogParser,
+   OS, Promise, volumeService, XPCOMUtils, SystemAppProxy */
+
+'use strict';
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyModuleGetter(this, 'LogCapture', 'resource://gre/modules/LogCapture.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'LogParser', 'resource://gre/modules/LogParser.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'OS', 'resource://gre/modules/osfile.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'Promise', 'resource://gre/modules/Promise.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'Services', 'resource://gre/modules/Services.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'SystemAppProxy', 'resource://gre/modules/SystemAppProxy.jsm');
+
+XPCOMUtils.defineLazyServiceGetter(this, 'powerManagerService',
+                                   '@mozilla.org/power/powermanagerservice;1',
+                                   'nsIPowerManagerService');
+
+XPCOMUtils.defineLazyServiceGetter(this, 'volumeService',
+                                   '@mozilla.org/telephony/volume-service;1',
+                                   'nsIVolumeService');
+
+this.EXPORTED_SYMBOLS = ['LogShake'];
+
+function debug(msg) {
+  dump('LogShake.jsm: '+msg+'\n');
+}
+
+/**
+ * An empirically determined amount of acceleration corresponding to a
+ * shake
+ */
+const EXCITEMENT_THRESHOLD = 500;
+const DEVICE_MOTION_EVENT = 'devicemotion';
+const SCREEN_CHANGE_EVENT = 'screenchange';
+const CAPTURE_LOGS_ERROR_EVENT = 'capture-logs-error';
+const CAPTURE_LOGS_SUCCESS_EVENT = 'capture-logs-success';
+
+// Map of files which have log-type information to their parsers
+const LOGS_WITH_PARSERS = {
+  '/dev/__properties__': LogParser.prettyPrintPropertiesArray,
+  '/dev/log/main': LogParser.prettyPrintLogArray,
+  '/dev/log/system': LogParser.prettyPrintLogArray,
+  '/dev/log/radio': LogParser.prettyPrintLogArray,
+  '/dev/log/events': LogParser.prettyPrintLogArray,
+  '/proc/cmdline': LogParser.prettyPrintArray,
+  '/proc/kmsg': LogParser.prettyPrintArray,
+  '/proc/meminfo': LogParser.prettyPrintArray,
+  '/proc/uptime': LogParser.prettyPrintArray,
+  '/proc/version': LogParser.prettyPrintArray,
+  '/proc/vmallocinfo': LogParser.prettyPrintArray,
+  '/proc/vmstat': LogParser.prettyPrintArray
+};
+
+let LogShake = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  /**
+   * If LogShake is listening for device motion events. Required due to lag
+   * between HAL layer of device motion events and listening for device motion
+   * events.
+   */
+  deviceMotionEnabled: false,
+
+  /**
+   * If a capture has been requested and is waiting for reads/parsing. Used for
+   * debouncing.
+   */
+  captureRequested: false,
+
+  /**
+   * Start existing, observing motion events if the screen is turned on.
+   */
+  init: function() {
+    // TODO: no way of querying screen state from power manager
+    // this.handleScreenChangeEvent({ detail: {
+    //   screenEnabled: powerManagerService.screenEnabled
+    // }});
+
+    // However, the screen is always on when we are being enabled because it is
+    // either due to the phone starting up or a user enabling us directly.
+    this.handleScreenChangeEvent({ detail: {
+      screenEnabled: true
+    }});
+
+    SystemAppProxy.addEventListener(SCREEN_CHANGE_EVENT, this, false);
+
+    Services.obs.addObserver(this, 'xpcom-shutdown', false);
+  },
+
+  /**
+   * Handle an arbitrary event, passing it along to the proper function
+   */
+  handleEvent: function(event) {
+    switch (event.type) {
+    case DEVICE_MOTION_EVENT:
+      if (!this.deviceMotionEnabled) {
+        return;
+      }
+      this.handleDeviceMotionEvent(event);
+      break;
+
+    case SCREEN_CHANGE_EVENT:
+      this.handleScreenChangeEvent(event);
+      break;
+    }
+  },
+
+  /**
+   * Handle an observation from Services.obs
+   */
+  observe: function(subject, topic) {
+    if (topic === 'xpcom-shutdown') {
+      this.uninit();
+    }
+  },
+
+  startDeviceMotionListener: function() {
+    if (!this.deviceMotionEnabled) {
+      SystemAppProxy.addEventListener(DEVICE_MOTION_EVENT, this, false);
+      this.deviceMotionEnabled = true;
+    }
+  },
+
+  stopDeviceMotionListener: function() {
+    SystemAppProxy.removeEventListener(DEVICE_MOTION_EVENT, this, false);
+    this.deviceMotionEnabled = false;
+  },
+
+  /**
+   * Handle a motion event, keeping track of 'excitement', the magnitude
+   * of the device's acceleration.
+   */
+  handleDeviceMotionEvent: function(event) {
+    // There is a lag between disabling the event listener and event arrival
+    // ceasing.
+    if (!this.deviceMotionEnabled) {
+      return;
+    }
+
+    var acc = event.accelerationIncludingGravity;
+
+    var excitement = acc.x * acc.x + acc.y * acc.y + acc.z * acc.z;
+
+    if (excitement > EXCITEMENT_THRESHOLD) {
+      if (!this.captureRequested) {
+        this.captureRequested = true;
+        captureLogs().then(logResults => {
+          // On resolution send the success event to the requester
+          SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_SUCCESS_EVENT, {
+            logFilenames: logResults.logFilenames,
+            logPrefix: logResults.logPrefix
+          });
+          this.captureRequested = false;
+        },
+        error => {
+          // On an error send the error event
+          SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_ERROR_EVENT, {error: error});
+          this.captureRequested = false;
+        });
+      }
+    }
+  },
+
+  handleScreenChangeEvent: function(event) {
+    if (event.detail.screenEnabled) {
+      this.startDeviceMotionListener();
+    } else {
+      this.stopDeviceMotionListener();
+    }
+  },
+
+  /**
+   * Stop logshake, removing all listeners
+   */
+  uninit: function() {
+    this.stopDeviceMotionListener();
+    SystemAppProxy.removeEventListener(SCREEN_CHANGE_EVENT, this, false);
+    Services.obs.removeObserver(this, 'xpcom-shutdown');
+  }
+};
+
+function getLogFilename(logLocation) {
+  // sanitize the log location
+  let logName = logLocation.replace(/\//g, '-');
+  if (logName[0] === '-') {
+    logName = logName.substring(1);
+  }
+  return logName + '.log';
+}
+
+function getSdcardPrefix() {
+  return volumeService.getVolumeByName('sdcard').mountPoint;
+}
+
+function getLogDirectory() {
+  let d = new Date();
+  d = new Date(d.getTime() - d.getTimezoneOffset() * 60000);
+  let timestamp = d.toISOString().slice(0, -5).replace(/[:T]/g, '-');
+  // return directory name of format 'logs/timestamp/'
+  return OS.Path.join('logs', timestamp, '');
+}
+
+/**
+ * Captures and saves the current device logs, returning a promise that will
+ * resolve to an array of log filenames.
+ */
+function captureLogs() {
+  let logArrays = readLogs();
+  return saveLogs(logArrays);
+}
+
+/**
+ * Read in all log files, returning their formatted contents
+ */
+function readLogs() {
+  let logArrays = {};
+  for (let loc in LOGS_WITH_PARSERS) {
+    let logArray = LogCapture.readLogFile(loc);
+    if (!logArray) {
+      continue;
+    }
+    let prettyLogArray = LOGS_WITH_PARSERS[loc](logArray);
+
+    logArrays[loc] = prettyLogArray;
+  }
+  return logArrays;
+}
+
+/**
+ * Save the formatted arrays of log files to an sdcard if available
+ */
+function saveLogs(logArrays) {
+  if (!logArrays || Object.keys(logArrays).length === 0) {
+    return Promise.resolve({
+      logFilenames: [],
+      logPrefix: ''
+    });
+  }
+
+  let sdcardPrefix, dirName;
+  try {
+    sdcardPrefix = getSdcardPrefix();
+    dirName = getLogDirectory();
+  } catch(e) {
+    // Return promise failed with exception e
+    // Handles missing sdcard
+    return Promise.reject(e);
+  }
+
+  debug('making a directory all the way from '+sdcardPrefix+' to '+(sdcardPrefix + '/' + dirName));
+  return OS.File.makeDir(OS.Path.join(sdcardPrefix, dirName), {from: sdcardPrefix})
+    .then(function() {
+    // Now the directory is guaranteed to exist, save the logs
+    let logFilenames = [];
+    let saveRequests = [];
+
+    for (let logLocation in logArrays) {
+      debug('requesting save of ' + logLocation);
+      let logArray = logArrays[logLocation];
+      // The filename represents the relative path within the SD card, not the
+      // absolute path because Gaia will refer to it using the DeviceStorage
+      // API
+      let filename = dirName + getLogFilename(logLocation);
+      logFilenames.push(filename);
+      let saveRequest = OS.File.writeAtomic(OS.Path.join(sdcardPrefix, filename), logArray);
+      saveRequests.push(saveRequest);
+    }
+
+    return Promise.all(saveRequests).then(function() {
+      debug('returning logfilenames: '+logFilenames.toSource());
+      return {
+        logFilenames: logFilenames,
+        logPrefix: dirName
+      };
+    });
+  });
+}
+
+LogShake.init();
+this.LogShake = LogShake;
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -47,16 +47,19 @@ if CONFIG['MOZ_UPDATER']:
     ]
 
 EXTRA_JS_MODULES += [
     'AlertsHelper.jsm',
     'AppFrames.jsm',
     'ContentRequestHelper.jsm',
     'ErrorPage.jsm',
     'FxAccountsMgmtService.jsm',
+    'LogCapture.jsm',
+    'LogParser.jsm',
+    'LogShake.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
     'WebappsUpdater.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     EXTRA_JS_MODULES += [
new file mode 100644
index 0000000000000000000000000000000000000000..b1ed7f10ae86e16e9e6f1b14006182ceb278f5e3
GIT binary patch
literal 4037
zc%1E*zi!h|6o+p_(;=#aSn0rNg%k(kI!QqlAPNzsfKV$Sf`tLy`ko|Koa@McB47(M
z8xN3~M_}kwsiHz+gEwGd?gQW($Hhr)a$}MK(IJlFocrDLJB~jqc|yqfOz>K|utYM>
z@#TdVmdS`XKxM5AirYl6v5UNc1*jXg!5iRp$b*=r=fb648v$RtXme_U<5CwvZ&Kb6
zYgM25h#Rg!P2;V9q5$e9lt9_F)E#67Es*<@&<@k7&87!$55d(Coc~mi07n^uDfVF|
zs?T}b^0<racgrEUxBv8t1o<>c@GGD^LC!>k)8L=)u1bI-h=h)p$7j)7E@lVdEw5u5
zy1)fcrCi9JmH(UNy{#YWS^0lfULBS{6Al&S2S=H}>KenYwr5&;OY8cdwerS|o9iXm
zL*WeF;*P=4Mh11uhHlg^xf6Frp{WhG4$8eI>aBs|F2<09bhVbz=?F^Q@ri!DlOrz^
z!);;L&=KM@7Qv-<16|M@*Rp8?wV>Y;4Eut+ptZTHIjDQK|E)_!|Kv`(*wKI@@HJ3&
zLi0v1&$o8Jw!m*)l0Zfff_NR2;Y<fZyd8ml7tmEQe3Fj`*48Lzwq>waK~!)&9UZ6c
zndTet!plU|WME`W)PI)+eEg7)@96IVCySye@-PM<G3e3BCOkfO)3M`khYe6_P3qrx
zNjvOY{o0pOc12=`I95W2>evpCkH4<Y)Dt0e_MQmIa-J9sH+N4=5nI+1pMB(^+z&wB
z6GuLDdu4{67z0l42?03VEGnrzF##~y6Cq$8o|uB2(i4k%zm}~~nZF(>Jt1~je1A}!
ZsV73{>^%{Z<vcMOZtk9#BKDs=@eBW@U8n#6
new file mode 100644
index 0000000000000000000000000000000000000000..a8254853738e2083fbea3c659e4346c141214f2f
GIT binary patch
literal 65536
zc%1EB+j`r^wMHb_a$-BNb84D{qo!`!rX)yEltj6w?bvQ(*S1{AahjgcAut4C;Uo+|
zitfJPx1Vf3!oJ$?u<!Q+^dFEE0T56eiv#syetjSe%mDu!*Q_<O2L3-qQT~5TQJ(DY
zJsIrXxu@iDNqK90Nx7!HuDm^dUAdyXp<El|P%bN%$2f53^7x8!6@Qn<IFvV)H^*-x
z_FKyP<F|14n)31Zn)04<9oN@!^)}x3w(`F64&uLq-**xIE`HxremH(l`2ZnT$L}Nd
z4{*kTJ6Fdzlplf@hw>qw{}ACH;rb)I?PKMg@yCe$iSpt26U4rO`!^8&DbAnby&KBA
z;|<*Zk#c2>L-`Ep_zX||7`!-?n>gRZ(?3x@8RJks$N6)l;-|>(PjUS-r0Zvh_jBdF
z@y~Jp3+1En7dZa{;lBWH4f(AheRbu7aUG?jB0VbD8+cCx&o%M>CayJHYbc!-o^K(y
zn@Ha#a@9usHqyC;^A@hRamJzSAl?q%-$A?%p5Ik&jCXOqh45R*-!F0gCBlD&eE$m1
z{Tk`{HTZsm=YE6xzeT!#i}?S6bmLHND?b?DMmc<`yfOX~*LRSfJK(vC=kB6Z@8Lc7
z5dU|G|2v$2k82#tSE#42kgxl=zK`c0pgbSo{vUAt2c+X6>gOS9`w`yr2<OL${}|zW
z;M+qvJ;51=@<)^p4rL$beOw>lJ`Uxn^6B^~&R^p_U*r4?>3)Xt_(r)t{s!gxC%pGh
zNawexzi&}~e@47NgZD28{|mzZitxXpUjK&o{SEEo@3{Uu!gbV(j(q$B<@XOfPf+g!
z<<!M>7w<6;jzck#9}{PaGevk0;XRx!@ZeDT$Y&qdHqvF|{m&8p9M^}4e~9vSP+m9`
z7xnDoJs$FpL-CPbANdGSPXXHL5z=>rbBJ?@^f5fg5I;gbB9vQ<c8x<B;5<P15bqx%
z{xPnP@tzUljllB)>Bgb_6Yu{gdgp&4jS><P60d;JSG&F+sfO<{-=RfO)}v_j)xpE2
zQ2ss%%^?Yk60mk;_^uH06hHrQd}7jYA<C79+S5C&W>eFezh8PG%=oG68YNNIqA)J3
zm}T-a8rsD1{DLT#I$FDDP_iYGze^bH63;A%vg$Hn)zA4)$$Ek+^6&a)X%(->-l69Y
zJrVQ?e|ga-5MBPKbiD+k``;;|CxK}B6VidrY`?UAUlSvFPxyPpi2QIAQ5KyGKchxm
zUi#wsiR1V~)%A@-)$t7y<*VW~O)S4jyRl`FZt>_Vm_MK)vl-<7h^Z_JZ6nhCK&M_R
zITw1wVWI}0H2<(~N7S+N&2XcEz=*mH8X1j~I64UeKVa&y>nw{uW?ila^=xY%zM?2w
zrcJEcWR%dEYtH@&wWt=l9*y)aaceGNOlRmw#G*A~c3+a;_H2+33^k?$>P1CDcs-0g
z&-Sc>ptA$*&j?3Bgl-s-sIZnV#~$^PNVIf}cx4QH$5aC!_1R;(Ng|?~zL!mg=>FG@
zZKT02&D+HX2M5~L1Ht^J%>svv3evILbBIgp)U!6)>gL8IH5<58pZEQ)?WwJ%)^2V$
zcT`QoFuA?KC3*djj@b9%AMlRgd#a79rY@Qynf0p=%c+)_{~13s4|7pgw9eUiUYg&4
zCg!2M=TTPo?>(%JKco&N8F9OcS{Ki6mh^r_`S^fD8xLY{V`FDS({8nzx3ry&CwHH2
z@W!xIel?B?jG#Br<XYPHR!iGCE88jj(I_a%|0)$Edo%noE7N~3PWql;{=Q$L{wZ&D
zeHS)D6thG>I@54l&L0u0L|(oAedO6I@CTu9#zv%a8>}dE7ZUj^fIG+E4U3I1(e}rT
z-z&*wRrxnz2N?xCm6g8_L$Dyq`lEwKoo!9n{MU@%2x66V?S&|pN$64p)CW7H0{&J$
z%KDUl>D)Pfmv}L^MPUqPB;jh*5<34d<4=SeRUP74F_id|C@WNu>@DUGsg<=rSH3<F
z%0IMwwrcc==TS$5i2W}9q~EDfexsfr7S!~*%UqHDZ!-Q&YoQ>DX!$eMe<(9TJ6M=~
zs7n9u!NR0r5ZYe84DRyrr;#LL@_P%(XiSpPC{M;M1U0@hDJT4BfFU|ROlua|Mmqkw
zNqZ!AO0<(RBIMkT-3>_8FNt!a0n6O<!v>=91CP{?jQWvl*EN-uP8)weeiD|cKgyN5
zxzWJj*kG=?^iD9F|47o|+0hF<a9n}%?z{ON-?MbzW4g#`zn9?`IqmQ={0jqd#r)!o
z&QtkW-w&h88o+7%RWyLp_^W6Dr}0<O0KluVxO+18C3bjL60SdfR@?vj{v#p$ckeyB
zf9Ib5WPk73{e$~^kMG@Gh*HV?dq_;%S6yaBG1tTRY-~6k@hX{rpYTHk8=7fu;rtl?
zBkItg?|UP4I?(GLjfQ?Wn+JSN$inLs$>NL3dTOt$6K6<9OwTJ|LhFCR4+SgmnSIg$
zGEr8|k+=bQ!f%Cq5zK%I7U(Dlm%4s9nne-1{x;$74-FN$eA*8w#E+A;!dEp_TnjLX
zGq4ZUfvff@37GvN*U(oKWv#WfEzAIgUZ(~w&%Dlv8#vIy!`w`)==G1j>Dp>$#V-q?
z{wGs$XHuio1ELCk5<~nAY?JznNf9dloPeB~`6~EhM(dFI7VU+$)nBl)gqFWcO`E7G
z_GwG3^Tn{qJka^@_oFD7L^&URLS{=L=fi(w1n0`%b4kk#&X1oNWIF1K<#&TAqq=Do
zEBqDYAB|1l(sVzLf;gIbf1C`$g7ZJiKA&aM0}?K*gI(C%Y43^}10nN`i3g7A&_Ut)
zPbK>QOan85Njg%tm+0{y;(y|K0kzw;7MDPT*8eR3k&TvEtc8g7fAs_ESq^2hCHQ=l
zi`wq?c2kfK!rb_ABD&|@F<o*uw+>cG`|I&mz)YiH2~2eP9~s=~W1@noj4iB!i?%;A
z{5>b8o<Y+_*qWF3NLnsGcRlM3)vm=|+L-RxuAR}rR$2lLFrNRIIFP-rYM}EKM5(0x
znS}LWejckK4RA2uiDye8m$X)=`NfWyQYhr--q1taoURD0x0?w>&}aF{fQ>vu9onAB
zJyID>RKQhD5I0BK{(<-{GDJl8zn-f`A@LZReN^NFBbMKt<^9;8$qeMeGK^69cSuK8
z%-26XUk~X~Y=;oTc4oRkwERWiSJB`@-%h-+Nazzc?QU8zKjo{yBqaggc=V`ln)MMN
zh@ZRssv+_m6-oN*`v}nwMqbp{n{Cy%mgx;P=lA@`?%5OnbDjC|i7(LAz&^IA8AD1e
zmwU=TS=~ejh^WW)ADypplKK4K2f6jnBctvI(V161C&<me@tNoRB-~0zocl+P-wOR<
z)X$B;tGnW+|8o4uzC|O9e2)F%WFx{_Q{53Z0h;AUzwd=KKLsk#{U*;(jtlrj)<4?6
z>%UNa_gL)@xdNci2mUG6xc*Tcf1l<i;Z_ByJ~Mvk{Gnaue5#1we>RnW#37-p6NiSm
z8OZCmlaM<OVazs`ECX}vZ?O~E7=AO`K-NrV3iFGa)<5@s^ny5gPK><!yKwuT^Lsr*
z%}x89K=k;}`S~(%Q=P8+>De{l*DdDOh6XHSvHV&I|EeW+|Kp6Gx)jpiGe&xMq^EqI
zsw#gkb`8g-UX*L>uZi?SNb#rL0CaS~DD)F^RA~PF&{Dgw`Cm-kk9p6}t-)S^d?W%?
z&jiT-Aq^7~Y^nkqeQF#&_vY@lLi<0OyxJ}I{pMJ|JxQ4`e(rl}mC*k$^j{vw^w=yK
z)WgJQ6~^n?`RDovbWh{34olRa^`4!Yg?zWct)d3;!bTFx)aZC=&WJhx3$v>`zLj!8
zSQEVdp5Fh+=U`tfT0qxsLomTGX*{Mw{oA=;#!~qw`KK_yv*XZOrT#A_q>i4<hqF1q
zX!*-}s$-at_^`gB0y(qVY75o>CW;r|g<l*+X!#Ev;;Ck64``^CxZnzH|7rgG-rp%c
zoAiIzW=4a{oyIqJ9v>X&$$FL1vsBwV!`6tSWcDMOf#O?%i=tfO-a!s6+n#qep#Hh;
z7x;Otdyduntve#96L8rc8G+i3dp+J2A=~rr-al?PceiVuoy|_OrfJPqvr|j%Xxp2e
z_Vms&`6pw`6f25S+iJC2oldRQ+}+W(TD7*e+um%pYpv~;)^5*2wPw4swe!7q#1`;7
zxth)~oTNh(f9z&8Kwkds3d_IGf{dpBlQe&;)!fb{U~+4j{Bb4$)$^O@%pZ^-6{Tu^
zYG#$eAAxL>w|4MvZZ+G@G<$O66}e0Jtsu;_|4%{JLjJdT^WW}F>F3vHU@y-9z|2go
zru*Nt&!-Ata_5!#C!f~PC!e86W?$p52PL=XB>RDQ6rtnq$@ec94Y~d05|3Ef$ta=U
zzc}F^<lRmy=I4q)-GJR2(mI>#K^TA5S>y+SfNyVP_$SJb&TZics^BaImGLL}k6AaJ
z`6~I@kOaYEEmy`5L2$0^Z}Jr)ns@(LOYw_H!U;d$Rc8|F=KOCiR+j&wt)~g9;ZGd#
z7Q@#n%YQd6ndGj9-}W&4I7KqJ8h*Y>HS&wTLsktxtgv(K|Ca9^GNWH0=BnZEwh9UU
zC(SQX{!GVTm)PF9jK9gQVs~LCp)!6uWQNXHp3jM&m@Ei`qCOzB{Uskiu)T;TT5n;L
z3w(}9ln$KP|7Jh{>f8@yvJe?eCZUDSzh&D$TbO{WQ2!$?4G9}8EJRl$e};y~Sbb`R
zRL7t8zdX^sRh0KUiqQGzwEkc60cfH5FZ=Fd3e|jnG1>ZB7)5CNk6JEFk22Tu1XsQk
z`2E}Y_~S;i;>$&;$o_TX<C5~Pgnw>-<d<8|q5t=LmG}RW%^%A7<eycZe~66p(7$K$
zuk`Dyg5_Uv`3K$946yM1AB+6GsH*xe=J!a2{Bv5CnRl<A-;9e!AmRIO7R%o~pYn&;
z<s)$}iqP*5Wbz-WeZ$t1%@C;t*;Vv;q5MX=2}TPZh!+z7C_((D?{r7Ay<fI>2wSX|
zUj0bykARZHQyf&!0`7QrWZ+j1LYgdt=#Fn3>U}zyin=KN`}2wK+rWvac3c=mbpM<1
zo2BmjV)?n_qhTz5^?i!}Qj{CJnpV@aR;}4=)|wr@;#}L^-Nl*zdv|I4g>H8LEtub8
z?)=lwP(f`s$jrbzP?T$boC=7I{re9$G<B)Xao+Q7cCWwz_-2LvKU@bYn1o*Om+Enr
z#oYc6h|c05aBPF5K7r^|p#O2#D}CnVlN0qf1^a(y=YRNz`0E)@um^(eU$bunYBIsb
zcYQ8?<?&%v_0JPxn0kJ$FA`k;sr&~;F1Eb?!S>%gqnLRa{*V&3Fa?3~zqo(z%e}p)
z!ud^)Ew=Q>DF2R{{8hle=;{8s5Y{sO#V_tv%fIMHQYF977he|n-?w7QJ(PRC#`g-?
zjziR?eGxsyZwJIQA%rp`5cscZO|`Y9shiuXw!3UyFvV|?hz|1`4ZweS#@|wxKN6()
zUBVUxEzf^zYfDXzc4xWDN0vYNe=oQXdXE2s*6aw|0iWXMbN>rgMz;Ky{~u7K_$Rw;
z?`Q2F(fyD6pPeFp`S>GL{$}_KHX3pMrkeZ-=C_7L;Wq{Mzv<qV(ic}z{;9n+Mf@vg
zwQ(dQBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsx
zBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsx
jBqSsxBqSsxBqSsxBqSsxBqSsxBqSsxBqSsx{$24ea`HxF
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_logcapture.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+/**
+ * Test that LogCapture successfully reads from the /dev/log devices, returning
+ * a Uint8Array of some length, including zero. This tests a few standard
+ * log devices
+ */
+function run_test() {
+  Components.utils.import('resource:///modules/LogCapture.jsm');
+
+  function verifyLog(log) {
+    // log exists
+    notEqual(log, null);
+    // log has a length and it is non-negative (is probably array-like)
+    ok(log.length >= 0);
+  }
+
+  let mainLog = LogCapture.readLogFile('/dev/log/main');
+  verifyLog(mainLog);
+
+  let meminfoLog = LogCapture.readLogFile('/proc/meminfo');
+  verifyLog(meminfoLog);
+}
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_logparser.js
@@ -0,0 +1,49 @@
+/* jshint moz: true */
+
+const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
+
+function run_test() {
+  Cu.import('resource:///modules/LogParser.jsm');
+
+  let propertiesFile = do_get_file('data/test_properties');
+  let loggerFile = do_get_file('data/test_logger_file');
+
+  let propertiesStream = makeStream(propertiesFile);
+  let loggerStream = makeStream(loggerFile);
+
+  // Initialize arrays to hold the file contents (lengths are hardcoded)
+  let propertiesArray = new Uint8Array(propertiesStream.readByteArray(65536));
+  let loggerArray = new Uint8Array(loggerStream.readByteArray(4037));
+
+  propertiesStream.close();
+  loggerStream.close();
+
+  let properties = LogParser.parsePropertiesArray(propertiesArray);
+  let logMessages = LogParser.parseLogArray(loggerArray);
+
+  // Test arbitrary property entries for correctness
+  equal(properties['ro.boot.console'], 'ttyHSL0');
+  equal(properties['net.tcp.buffersize.lte'],
+        '524288,1048576,2097152,262144,524288,1048576');
+
+  ok(logMessages.length === 58, 'There should be 58 messages in the log');
+
+  let expectedLogEntry = {
+    processId: 271, threadId: 271,
+    seconds: 790796, nanoseconds: 620000001, time: 790796620.000001,
+    priority: 4, tag: 'Vold',
+    message: 'Vold 2.1 (the revenge) firing up\n'
+  };
+
+  deepEqual(expectedLogEntry, logMessages[0]);
+}
+
+function makeStream(file) {
+  var fileStream = Cc['@mozilla.org/network/file-input-stream;1']
+                .createInstance(Ci.nsIFileInputStream);
+  fileStream.init(file, -1, -1, 0);
+  var bis = Cc['@mozilla.org/binaryinputstream;1']
+                .createInstance(Ci.nsIBinaryInputStream);
+  bis.setInputStream(fileStream);
+  return bis;
+}
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_logshake.js
@@ -0,0 +1,141 @@
+/**
+ * Test the log capturing capabilities of LogShake.jsm
+ */
+
+/* jshint moz: true */
+/* global Components, LogCapture, LogShake, ok, add_test, run_next_test, dump */
+/* exported run_test */
+
+/* disable use strict warning */
+/* jshint -W097 */
+'use strict';
+
+const Cu = Components.utils;
+
+Cu.import('resource://gre/modules/LogCapture.jsm');
+Cu.import('resource://gre/modules/LogShake.jsm');
+
+// Force logshake to handle a device motion event with given components
+// Does not use SystemAppProxy because event needs special
+// accelerationIncludingGravity property
+function sendDeviceMotionEvent(x, y, z) {
+  let event = {
+    type: 'devicemotion',
+    accelerationIncludingGravity: {
+      x: x,
+      y: y,
+      z: z
+    }
+  };
+  LogShake.handleEvent(event);
+}
+
+// Send a screen change event directly, does not use SystemAppProxy due to race
+// conditions.
+function sendScreenChangeEvent(screenEnabled) {
+  let event = {
+    type: 'screenchange',
+    detail: {
+      screenEnabled: screenEnabled
+    }
+  };
+  LogShake.handleEvent(event);
+}
+
+function debug(msg) {
+  var timestamp = Date.now();
+  dump('LogShake: ' + timestamp + ': ' + msg);
+}
+
+add_test(function test_do_log_capture_after_shaking() {
+  // Enable LogShake
+  LogShake.init();
+
+  let readLocations = [];
+  LogCapture.readLogFile = function(loc) {
+    readLocations.push(loc);
+    return null; // we don't want to provide invalid data to a parser
+  };
+
+  // Fire a devicemotion event that is of shake magnitude
+  sendDeviceMotionEvent(9001, 9001, 9001);
+
+  ok(readLocations.length > 0,
+      'LogShake should attempt to read at least one log');
+
+  LogShake.uninit();
+  run_next_test();
+});
+
+add_test(function test_do_nothing_when_resting() {
+  // Enable LogShake
+  LogShake.init();
+
+  let readLocations = [];
+  LogCapture.readLogFile = function(loc) {
+    readLocations.push(loc);
+    return null; // we don't want to provide invalid data to a parser
+  };
+
+  // Fire a devicemotion event that is relatively tiny
+  sendDeviceMotionEvent(0, 9.8, 9.8);
+
+  ok(readLocations.length === 0,
+      'LogShake should not read any logs');
+
+  debug('test_do_nothing_when_resting: stop');
+  LogShake.uninit();
+  run_next_test();
+});
+
+add_test(function test_do_nothing_when_disabled() {
+  debug('test_do_nothing_when_disabled: start');
+  // Disable LogShake
+  LogShake.uninit();
+
+  let readLocations = [];
+  LogCapture.readLogFile = function(loc) {
+    readLocations.push(loc);
+    return null; // we don't want to provide invalid data to a parser
+  };
+
+  // Fire a devicemotion event that would normally be a shake
+  sendDeviceMotionEvent(0, 9001, 9001);
+
+  ok(readLocations.length === 0,
+      'LogShake should not read any logs');
+
+  run_next_test();
+});
+
+add_test(function test_do_nothing_when_screen_off() {
+  // Enable LogShake
+  LogShake.init();
+
+
+  // Send an event as if the screen has been turned off
+  sendScreenChangeEvent(false);
+
+  let readLocations = [];
+  LogCapture.readLogFile = function(loc) {
+    readLocations.push(loc);
+    return null; // we don't want to provide invalid data to a parser
+  };
+
+  // Fire a devicemotion event that would normally be a shake
+  sendDeviceMotionEvent(0, 9001, 9001);
+
+  ok(readLocations.length === 0,
+      'LogShake should not read any logs');
+
+  // Restore the screen
+  sendScreenChangeEvent(true);
+
+  LogShake.uninit();
+  run_next_test();
+});
+
+function run_test() {
+  debug('Starting');
+  run_next_test();
+}
--- a/b2g/components/test/unit/xpcshell.ini
+++ b/b2g/components/test/unit/xpcshell.ini
@@ -1,14 +1,24 @@
 [DEFAULT]
 head =
 tail =
 
+support-files =
+  data/test_logger_file
+  data/test_properties
+
 [test_bug793310.js]
 
 [test_bug832946.js]
 
 [test_fxaccounts.js]
 [test_signintowebsite.js]
 head = head_identity.js
 tail =
 
+[test_logcapture.js]
+# only run on b2g builds due to requiring b2g-specific log files to exist
+skip-if = toolkit != "gonk"
 
+[test_logparser.js]
+
+[test_logshake.js]
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4b4336c73c081b39776d399835ce4853aee5cc1c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -122,17 +122,17 @@
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="118dec582647895a863dbbce8ec26bc7af457bbe"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
   <default remote="sprd-aosp" revision="sprdb2g_gonk4.4" sync-j="4"/>
   <!-- Stock Android things -->
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
-  <project name="device/sprd" path="device/sprd" revision="4eac1e31bf69596bf229a6877c9aa3493ecd9fce"/>
+  <project name="device/sprd" path="device/sprd" revision="3ba0b8b4e3f55d68f84603218e522d054a723532"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="facca8d3e35431b66f85a4eb42bc6c5b24bd04da"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="03effd4db31881b2dd8d57575694515327f9ea2e"/>
   <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
   <project name="u-boot" path="u-boot" revision="df509ae75495aa409969973b97ee781037dee1fe"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="74465af039d2809454afdfef285285bb63146e1b">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4b4336c73c081b39776d399835ce4853aee5cc1c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4b4336c73c081b39776d399835ce4853aee5cc1c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="74465af039d2809454afdfef285285bb63146e1b">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "a0fe2fdc571f1a5eb5474e08773fac80f9bb9fbd", 
+    "revision": "dc9488c3826ae1dbd0051ac19c83c5da1f4915a7", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="74465af039d2809454afdfef285285bb63146e1b">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="52670853c17fc0d3d33065c667c0ce124c93b98f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="af04d8bc2111d4ea146239a89ff602206b85eaf5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7ddb07033043613303061416882c9b02ac3d76b6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2aa3a0d353812bb8732a122965ffdbf5f889ca12"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -436,16 +436,18 @@
 @BINPATH@/components/MmsService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/TelephonyService.js
 @BINPATH@/components/TelephonyService.manifest
+@BINPATH@/components/MobileConnectionGonkService.js
+@BINPATH@/components/MobileConnectionGonkService.manifest
 #endif // MOZ_WIDGET_GONK && MOZ_B2G_RIL
 
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amInstallTrigger.js
 @BINPATH@/components/amWebInstallListener.js
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -108,22 +108,22 @@ MobileConnectionListener::NotifyUssdRece
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyDataError(const nsAString & message)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileConnectionListener::NotifyCFStateChange(bool success,
-                                              uint16_t action,
-                                              uint16_t reason,
-                                              const nsAString& number,
-                                              uint16_t timeSeconds,
-                                              uint16_t serviceClass)
+MobileConnectionListener::NotifyCFStateChanged(bool success,
+                                               uint16_t action,
+                                               uint16_t reason,
+                                               const nsAString& number,
+                                               uint16_t timeSeconds,
+                                               uint16_t serviceClass)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyEmergencyCbModeChanged(bool active,
                                                        uint32_t timeoutMs)
 {
@@ -149,28 +149,46 @@ MobileConnectionListener::NotifyRadioSta
 }
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyClirModeChanged(uint32_t aMode)
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MobileConnectionListener::NotifyLastKnownNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionListener::NotifyLastKnownHomeNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionListener::NotifyNetworkSelectionModeChanged()
+{
+  return NS_OK;
+}
+
 bool
 MobileConnectionListener::Listen(bool aStart)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE(provider, false);
+  nsCOMPtr<nsIMobileConnectionService> service =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(service, false);
 
   nsresult rv;
   if (aStart) {
-    rv = provider->RegisterMobileConnectionMsg(mClientId, this);
+    rv = service->RegisterListener(mClientId, this);
   } else {
-    rv = provider->UnregisterMobileConnectionMsg(mClientId, this);
+    rv = service->UnregisterListener(mClientId, this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  TelephonyListener Implementation
  */
@@ -347,23 +365,23 @@ BluetoothRilListener::Listen(bool aStart
 }
 
 void
 BluetoothRilListener::SelectClient()
 {
   // Reset mClientId
   mClientId = mMobileConnListeners.Length();
 
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(connection);
+  nsCOMPtr<nsIMobileConnectionService> service =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
 
   for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
     nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-    connection->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
+    service->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
     if (!voiceInfo) {
       BT_WARNING("%s: Failed to get voice connection info", __FUNCTION__);
       continue;
     }
 
     nsString regState;
     voiceInfo->GetState(regState);
     if (regState.EqualsLiteral("registered")) {
--- a/dom/bluetooth/BluetoothRilListener.h
+++ b/dom/bluetooth/BluetoothRilListener.h
@@ -7,17 +7,17 @@
 #ifndef mozilla_dom_bluetooth_bluetoothrillistener_h__
 #define mozilla_dom_bluetooth_bluetoothrillistener_h__
 
 #include "BluetoothCommon.h"
 
 #include "nsAutoPtr.h"
 
 #include "nsIIccProvider.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsITelephonyService.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothRilListener;
 
 class IccListener : public nsIIccListener
 {
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
@@ -607,18 +607,18 @@ BluetoothHfpManager::HandleVolumeChanged
     sBluetoothHfpInterface->VolumeControl(HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs,
                                           new VolumeControlResultHandler());
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIMobileConnectionService> connection =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
   connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -23,17 +23,17 @@
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #endif
 
 /**
  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
  * Capabilities" in Bluetooth hands-free profile 1.6
@@ -601,18 +601,18 @@ BluetoothHfpManager::HandleVolumeChanged
     SendCommand(RESPONSE_VGS, mCurrentVgs);
   }
 }
 
 #ifdef MOZ_B2G_RIL
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIMobileConnectionService> connection =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
   connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -33,37 +33,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Bluet
 
 // QueryInterface implementation for BluetoothAdapter
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
 
-/*
- * A comparator that does the comparison of BluetoothDevice instances.
- * Two BluetoothDevices are equivalent if they have an identical address.
- */
-class BluetoothDeviceComparator
-{
-public:
-  bool Equals(const BluetoothDevice* aDeviceA,
-              const BluetoothDevice* aDeviceB) const
-  {
-    nsString addressA;
-    nsString addressB;
-
-    aDeviceA->GetAddress(addressA);
-    aDeviceB->GetAddress(addressB);
-
-    return addressA.Equals(addressB);
-  }
-};
-
-class StartDiscoveryTask : public BluetoothReplyRunnable
+class StartDiscoveryTask MOZ_FINAL : public BluetoothReplyRunnable
 {
 public:
   StartDiscoveryTask(BluetoothAdapter* aAdapter, Promise* aPromise)
     : BluetoothReplyRunnable(nullptr, aPromise,
                              NS_LITERAL_STRING("StartDiscovery"))
     , mAdapter(aAdapter)
   {
     MOZ_ASSERT(aPromise);
@@ -204,20 +184,20 @@ public:
   }
 };
 
 static int kCreatePairedDeviceTimeout = 50000; // unit: msec
 
 BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
                                    const BluetoothValue& aValue)
   : DOMEventTargetHelper(aWindow)
-  , mDiscoveryHandleInUse(nullptr)
   , mState(BluetoothAdapterState::Disabled)
   , mDiscoverable(false)
   , mDiscovering(false)
+  , mDiscoveryHandleInUse(nullptr)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(IsDOMBinding());
 
   mPairingReqs = BluetoothPairingListener::Create(aWindow);
 
   const InfallibleTArray<BluetoothNamedValue>& values =
     aValue.get_ArrayOfBluetoothNamedValue();
@@ -299,17 +279,17 @@ BluetoothAdapter::SetPropertyByValue(con
       BT_APPEND_NAMED_VALUE(props, "Address", pairedDeviceAddresses[i]);
       BT_APPEND_NAMED_VALUE(props, "Paired", true);
 
       // Create paired device with 'address' and 'paired' attributes
       nsRefPtr<BluetoothDevice> pairedDevice =
         BluetoothDevice::Create(GetOwner(), BluetoothValue(props));
 
       // Append to adapter's device array if the device hasn't been created
-      if (!mDevices.Contains(pairedDevice, BluetoothDeviceComparator())) {
+      if (!mDevices.Contains(pairedDevice)) {
         mDevices.AppendElement(pairedDevice);
       }
     }
 
     // Retrieve device properties, result will be handled by device objects.
     GetPairedDeviceProperties(pairedDeviceAddresses);
   } else {
     BT_WARNING("Not handling adapter property: %s",
@@ -656,78 +636,93 @@ BluetoothAdapter::Pair(const nsAString& 
 
 already_AddRefed<Promise>
 BluetoothAdapter::Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv)
 {
   return PairUnpair(false, aDeviceAddress, aRv);
 }
 
 already_AddRefed<Promise>
-BluetoothAdapter::EnableDisable(bool aEnable, ErrorResult& aRv)
+BluetoothAdapter::Enable(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
-  // Ensure BluetoothService is available before modifying adapter state
+  /**
+   * Ensure
+   * - adapter is disabled, and
+   * - BluetoothService is available.
+   */
+  BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Disabled,
+                        NS_ERROR_DOM_INVALID_STATE_ERR);
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
 
-  // Modify adapter state to Enabling/Disabling if adapter is in a valid state
-  nsAutoString methodName;
-  if (aEnable) {
-    // Enable local adapter
-    BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Disabled,
-                          NS_ERROR_DOM_INVALID_STATE_ERR);
-    methodName.AssignLiteral("Enable");
-    mState = BluetoothAdapterState::Enabling;
-  } else {
-    // Disable local adapter
-    BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
-                          NS_ERROR_DOM_INVALID_STATE_ERR);
-    methodName.AssignLiteral("Disable");
-    mState = BluetoothAdapterState::Disabling;
-  }
-
-  // Notify applications of adapter state change to Enabling/Disabling
-  HandleAdapterStateChanged();
+  // Set adapter state "Enabling"
+  SetAdapterState(BluetoothAdapterState::Enabling);
 
   // Wrap runnable to handle result
   nsRefPtr<BluetoothReplyRunnable> result =
     new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
                                    promise,
-                                   methodName);
+                                   NS_LITERAL_STRING("Enable"));
 
-  if (NS_FAILED(bs->EnableDisable(aEnable, result))) {
-    // Restore mState and notify applications of adapter state change
-    mState = aEnable ? BluetoothAdapterState::Disabled
-                     : BluetoothAdapterState::Enabled;
-    HandleAdapterStateChanged();
-
+  if (NS_FAILED(bs->EnableDisable(true, result))) {
+    // Restore adapter state and reject promise
+    SetAdapterState(BluetoothAdapterState::Disabled);
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
-BluetoothAdapter::Enable(ErrorResult& aRv)
-{
-  return EnableDisable(true, aRv);
-}
-
-already_AddRefed<Promise>
 BluetoothAdapter::Disable(ErrorResult& aRv)
 {
-  return EnableDisable(false, aRv);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+
+  /**
+   * Ensure
+   * - adapter is enabled, and
+   * - BluetoothService is available.
+   */
+  BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
+                        NS_ERROR_DOM_INVALID_STATE_ERR);
+  BluetoothService* bs = BluetoothService::Get();
+  BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
+
+  // Set adapter state "Disabling"
+  SetAdapterState(BluetoothAdapterState::Disabling);
+
+  // Wrap runnable to handle result
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
+                                   promise,
+                                   NS_LITERAL_STRING("Disable"));
+
+  if (NS_FAILED(bs->EnableDisable(false, result))) {
+    // Restore adapter state and reject promise
+    SetAdapterState(BluetoothAdapterState::Enabled);
+    promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  return promise.forget();
 }
 
 BluetoothAdapterAttribute
 BluetoothAdapter::ConvertStringToAdapterAttribute(const nsAString& aString)
 {
   using namespace
     mozilla::dom::BluetoothAdapterAttributeValues;
 
@@ -763,18 +758,25 @@ BluetoothAdapter::IsAdapterAttributeChan
       return mDiscovering != aValue.get_bool();
     default:
       BT_WARNING("Type %d is not handled", uint32_t(aType));
       return false;
   }
 }
 
 void
-BluetoothAdapter::HandleAdapterStateChanged()
+BluetoothAdapter::SetAdapterState(BluetoothAdapterState aState)
 {
+  if (mState == aState) {
+    return;
+  }
+
+  mState = aState;
+
+  // Fire BluetoothAttributeEvent for changed adapter state
   nsTArray<nsString> types;
   BT_APPEND_ENUM_STRING(types,
                         BluetoothAdapterAttribute,
                         BluetoothAdapterAttribute::State);
   DispatchAttributeEvent(types);
 }
 
 void
@@ -811,20 +813,17 @@ BluetoothAdapter::HandleDeviceFound(cons
 {
   MOZ_ASSERT(mDiscoveryHandleInUse);
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
 
   // Create a temporary discovered BluetoothDevice to check existence
   nsRefPtr<BluetoothDevice> discoveredDevice =
     BluetoothDevice::Create(GetOwner(), aValue);
 
-  size_t index = mDevices.IndexOf(discoveredDevice,
-                                  0, /* aStart */
-                                  BluetoothDeviceComparator());
-
+  size_t index = mDevices.IndexOf(discoveredDevice);
   if (index == mDevices.NoIndex) {
     // New device, append it to adapter's device array
     mDevices.AppendElement(discoveredDevice);
   } else {
     // Existing device, discard temporary discovered device
     discoveredDevice = mDevices[index];
   }
 
@@ -852,20 +851,17 @@ BluetoothAdapter::HandlePairingRequest(c
 
   // Create a temporary device with deviceAddress for searching
   InfallibleTArray<BluetoothNamedValue> props;
   BT_APPEND_NAMED_VALUE(props, "Address", deviceAddress);
   nsRefPtr<BluetoothDevice> device =
     BluetoothDevice::Create(GetOwner(), props);
 
   // Find the remote device by address
-  size_t index = mDevices.IndexOf(device,
-                                  0, /* aStart */
-                                  BluetoothDeviceComparator());
-
+  size_t index = mDevices.IndexOf(device);
   if (index == mDevices.NoIndex) {
     BT_WARNING("Cannot find the remote device with address %s",
                NS_ConvertUTF16toUTF8(deviceAddress).get());
     return;
   }
 
   // Notify application of pairing requests
   mPairingReqs->DispatchPairingEvent(mDevices[index], passkey, type);
@@ -904,24 +900,21 @@ BluetoothAdapter::HandleDevicePaired(con
     BT_WARNING("HandleDevicePaired() is called when adapter isn't enabled.");
     return;
   }
 
   // Create paired device with 'address' and 'paired' attributes
   nsRefPtr<BluetoothDevice> pairedDevice =
     BluetoothDevice::Create(GetOwner(), aValue);
 
-  size_t index = mDevices.IndexOf(pairedDevice,
-                                  0, /* aStart */
-                                  BluetoothDeviceComparator());
-
-  if (index != mDevices.NoIndex) {
+  size_t index = mDevices.IndexOf(pairedDevice);
+  if (index == mDevices.NoIndex) {
+    mDevices.AppendElement(pairedDevice);
+  } else {
     pairedDevice = mDevices[index];
-  } else {
-    mDevices.AppendElement(pairedDevice);
   }
 
   // Notify application of paired device
   BluetoothDeviceEventInit init;
   init.mDevice = pairedDevice;
   DispatchDeviceEvent(NS_LITERAL_STRING("devicepaired"), init);
 }
 
@@ -934,26 +927,24 @@ BluetoothAdapter::HandleDeviceUnpaired(c
     BT_WARNING("HandleDeviceUnpaired() is called when adapter isn't enabled.");
     return;
   }
 
   // Create unpaired device with 'address' and 'paired' attributes
   nsRefPtr<BluetoothDevice> unpairedDevice =
     BluetoothDevice::Create(GetOwner(), aValue);
 
-  size_t index = mDevices.IndexOf(unpairedDevice,
-                                  0, /* aStart */
-                                  BluetoothDeviceComparator());
+  size_t index = mDevices.IndexOf(unpairedDevice);
 
   nsString deviceAddress;
-  if (index != mDevices.NoIndex) {
+  if (index == mDevices.NoIndex) {
+    unpairedDevice->GetAddress(deviceAddress);
+  } else {
     mDevices[index]->GetAddress(deviceAddress);
     mDevices.RemoveElementAt(index);
-  } else {
-    unpairedDevice->GetAddress(deviceAddress);
   }
 
   // Notify application of unpaired device
   BluetoothDeviceEventInit init;
   init.mAddress = deviceAddress;
   DispatchDeviceEvent(NS_LITERAL_STRING("deviceunpaired"), init);
 }
 
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -2,59 +2,52 @@
 /* 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/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothadapter_h__
 #define mozilla_dom_bluetooth_bluetoothadapter_h__
 
+#include "BluetoothCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BluetoothAdapter2Binding.h"
 #include "mozilla/dom/BluetoothDeviceEvent.h"
 #include "mozilla/dom/Promise.h"
-#include "BluetoothCommon.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 class DOMRequest;
 struct MediaMetaData;
 struct MediaPlayStatus;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothDevice;
 class BluetoothDiscoveryHandle;
-class BluetoothSignal;
 class BluetoothNamedValue;
 class BluetoothPairingListener;
+class BluetoothSignal;
 class BluetoothValue;
 
 class BluetoothAdapter : public DOMEventTargetHelper
                        , public BluetoothSignalObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothAdapter,
                                            DOMEventTargetHelper)
 
-  static already_AddRefed<BluetoothAdapter>
-  Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
-
-  void Notify(const BluetoothSignal& aParam);
-
-  void SetPropertyByValue(const BluetoothNamedValue& aValue);
-
-  virtual void DisconnectFromOwner() MOZ_OVERRIDE;
-
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
   BluetoothAdapterState State() const
   {
     return mState;
   }
 
   void GetAddress(nsString& aAddress) const
   {
     aAddress = mAddress;
@@ -78,116 +71,159 @@ public:
     return mDiscoverable;
   }
 
   BluetoothPairingListener* PairingReqs() const
   {
     return mPairingReqs;
   }
 
-  /**
-   * Update this adapter's discovery handle in use (mDiscoveryHandleInUse).
-   *
-   * |mDiscoveryHandleInUse| is set to the latest discovery handle when adapter
-   * just starts discovery, and is reset to nullptr when discovery is stopped
-   * by some adapter.
-   *
-   * @param aDiscoveryHandle [in] the discovery handle to set.
-   */
-  void SetDiscoveryHandleInUse(BluetoothDiscoveryHandle* aDiscoveryHandle);
+  /****************************************************************************
+   * Event Handlers
+   ***************************************************************************/
+  IMPL_EVENT_HANDLER(attributechanged);
+  IMPL_EVENT_HANDLER(devicepaired);
+  IMPL_EVENT_HANDLER(deviceunpaired);
+  IMPL_EVENT_HANDLER(a2dpstatuschanged);
+  IMPL_EVENT_HANDLER(hfpstatuschanged);
+  IMPL_EVENT_HANDLER(requestmediaplaystatus);
+  IMPL_EVENT_HANDLER(scostatuschanged);
+
+  /****************************************************************************
+   * Methods (Web API Implementation)
+   ***************************************************************************/
+  already_AddRefed<Promise> Enable(ErrorResult& aRv);
+  already_AddRefed<Promise> Disable(ErrorResult& aRv);
 
   already_AddRefed<Promise> SetName(const nsAString& aName, ErrorResult& aRv);
-  already_AddRefed<Promise>
-    SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
+  already_AddRefed<Promise> SetDiscoverable(bool aDiscoverable,
+                                            ErrorResult& aRv);
   already_AddRefed<Promise> StartDiscovery(ErrorResult& aRv);
   already_AddRefed<Promise> StopDiscovery(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-    Pair(const nsAString& aDeviceAddress, ErrorResult& aRv);
-  already_AddRefed<Promise>
-    Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv);
+  already_AddRefed<Promise> Pair(const nsAString& aDeviceAddress,
+                                 ErrorResult& aRv);
+  already_AddRefed<Promise> Unpair(const nsAString& aDeviceAddress,
+                                   ErrorResult& aRv);
 
   /**
    * Get a list of paired bluetooth devices.
    *
    * @param aDevices [out] Devices array to return
    */
   void GetPairedDevices(nsTArray<nsRefPtr<BluetoothDevice> >& aDevices);
 
-  already_AddRefed<Promise> EnableDisable(bool aEnable, ErrorResult& aRv);
-  already_AddRefed<Promise> Enable(ErrorResult& aRv);
-  already_AddRefed<Promise> Disable(ErrorResult& aRv);
-
+  // Connection related methods
   already_AddRefed<DOMRequest>
     Connect(BluetoothDevice& aDevice,
-            const Optional<short unsigned int>& aServiceUuid, ErrorResult& aRv);
+            const Optional<short unsigned int>& aServiceUuid,
+            ErrorResult& aRv);
   already_AddRefed<DOMRequest>
     Disconnect(BluetoothDevice& aDevice,
                const Optional<short unsigned int>& aServiceUuid,
                ErrorResult& aRv);
-  already_AddRefed<DOMRequest>
-    GetConnectedDevices(uint16_t aServiceUuid, ErrorResult& aRv);
+  already_AddRefed<DOMRequest> GetConnectedDevices(uint16_t aServiceUuid,
+                                                   ErrorResult& aRv);
 
+  // OPP file transfer related methods
+  already_AddRefed<DOMRequest> SendFile(const nsAString& aDeviceAddress,
+                                        nsIDOMBlob* aBlob,
+                                        ErrorResult& aRv);
+  already_AddRefed<DOMRequest> StopSendingFile(const nsAString& aDeviceAddress,
+                                               ErrorResult& aRv);
   already_AddRefed<DOMRequest>
-    SendFile(const nsAString& aDeviceAddress, nsIDOMBlob* aBlob,
-             ErrorResult& aRv);
-  already_AddRefed<DOMRequest>
-    StopSendingFile(const nsAString& aDeviceAddress, ErrorResult& aRv);
-  already_AddRefed<DOMRequest>
-    ConfirmReceivingFile(const nsAString& aDeviceAddress, bool aConfirmation,
+    ConfirmReceivingFile(const nsAString& aDeviceAddress,
+                         bool aConfirmation,
                          ErrorResult& aRv);
 
+  // SCO related methods
   already_AddRefed<DOMRequest> ConnectSco(ErrorResult& aRv);
   already_AddRefed<DOMRequest> DisconnectSco(ErrorResult& aRv);
   already_AddRefed<DOMRequest> IsScoConnected(ErrorResult& aRv);
 
+  // Handfree CDMA related methods
   already_AddRefed<DOMRequest> AnswerWaitingCall(ErrorResult& aRv);
   already_AddRefed<DOMRequest> IgnoreWaitingCall(ErrorResult& aRv);
   already_AddRefed<DOMRequest> ToggleCalls(ErrorResult& aRv);
 
+  // AVRCP related methods
   already_AddRefed<DOMRequest>
     SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv);
   already_AddRefed<DOMRequest>
-    SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv);
+    SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus,
+                        ErrorResult& aRv);
 
-  IMPL_EVENT_HANDLER(a2dpstatuschanged);
-  IMPL_EVENT_HANDLER(hfpstatuschanged);
-  IMPL_EVENT_HANDLER(requestmediaplaystatus);
-  IMPL_EVENT_HANDLER(scostatuschanged);
-  IMPL_EVENT_HANDLER(attributechanged);
-  IMPL_EVENT_HANDLER(devicepaired);
-  IMPL_EVENT_HANDLER(deviceunpaired);
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  static already_AddRefed<BluetoothAdapter>
+    Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
 
+  void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
   nsPIDOMWindow* GetParentObject() const
   {
      return GetOwner();
   }
 
-  virtual JSObject*
-    WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+  virtual void DisconnectFromOwner() MOZ_OVERRIDE;
+
+  /**
+   * Set this adapter's discovery handle in use (mDiscoveryHandleInUse).
+   *
+   * |mDiscoveryHandleInUse| is set to the latest discovery handle when adapter
+   * just starts discovery, and is reset to nullptr when discovery is stopped
+   * by some adapter.
+   *
+   * @param aDiscoveryHandle [in] Discovery handle to set.
+   */
+  void SetDiscoveryHandleInUse(BluetoothDiscoveryHandle* aDiscoveryHandle);
 
 private:
   BluetoothAdapter(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
   ~BluetoothAdapter();
 
-  already_AddRefed<Promise>
-    PairUnpair(bool aPair, const nsAString& aDeviceAddress, ErrorResult& aRv);
+  /**
+   * Set adapter properties according to properties array
+   *
+   * @param aValue [in] Properties array to set with
+   */
+  void SetPropertyByValue(const BluetoothNamedValue& aValue);
+
+  /**
+   * Set adapter state and fire BluetoothAttributeEvent if state changed.
+   *
+   * @param aState [in] The new adapter state
+   */
+  void SetAdapterState(BluetoothAdapterState aState);
 
-  bool IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
-                                 const BluetoothValue& aValue);
-  void HandleAdapterStateChanged();
-  void HandlePropertyChanged(const BluetoothValue& aValue);
-  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
-  BluetoothAdapterAttribute
-    ConvertStringToAdapterAttribute(const nsAString& aString);
+  /**
+   * Pair/Unpair adapter to device of given address.
+   * This function is called by methods Enable() and Disable().
+   *
+   * @param aPair          [in]  Whether to pair or unpair adapter to device.
+   * @param aDeviceAddress [in]  Address of device to pair/unpair.
+   * @param aRv            [out] Error result to set in case of error.
+   */
+  already_AddRefed<Promise> PairUnpair(bool aPair,
+                            const nsAString& aDeviceAddress,
+                            ErrorResult& aRv);
 
+  /**
+   * Retrieve properties of paired devices.
+   *
+   * @param aDeviceAddresses [in] Addresses array of paired devices
+   */
   void GetPairedDeviceProperties(const nsTArray<nsString>& aDeviceAddresses);
 
-  void HandleDeviceFound(const BluetoothValue& aValue);
-  void HandlePairingRequest(const BluetoothValue& aValue);
+  /**
+   * Handle "PropertyChanged" bluetooth signal.
+   *
+   * @param aValue [in] Array of changed properties
+   */
+  void HandlePropertyChanged(const BluetoothValue& aValue);
 
   /**
    * Handle DEVICE_PAIRED_ID bluetooth signal.
    *
    * @param aValue [in] Properties array of the paired device.
    *                    The array should contain two properties:
    *                    - nsString  'Address'
    *                    - bool      'Paired'
@@ -200,45 +236,116 @@ private:
    * @param aValue [in] Properties array of the unpaired device.
    *                    The array should contain two properties:
    *                    - nsString  'Address'
    *                    - bool      'Paired'
    */
   void HandleDeviceUnpaired(const BluetoothValue& aValue);
 
   /**
+   * Handle "DeviceFound" bluetooth signal.
+   *
+   * @param aValue [in] Properties array of the discovered device.
+   */
+  void HandleDeviceFound(const BluetoothValue& aValue);
+
+  /**
+   * Handle "PairingRequest" bluetooth signal.
+   *
+   * @param aValue [in] Array of information about the pairing request.
+   */
+  void HandlePairingRequest(const BluetoothValue& aValue);
+
+  /**
+   * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
+   */
+  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
+
+  /**
    * Fire BluetoothDeviceEvent to trigger
    * ondeviceparied/ondeviceunpaired event handler.
    *
    * @param aType [in] Event type to fire
    * @param aInit [in] Event initialization value
    */
   void DispatchDeviceEvent(const nsAString& aType,
                            const BluetoothDeviceEventInit& aInit);
 
   /**
-   * mDevices holds references of all created device objects.
-   * It is an empty array when the adapter state is disabled.
+   * Convert string to BluetoothAdapterAttribute.
+   *
+   * @param aString [in] String to convert
+   */
+  BluetoothAdapterAttribute
+    ConvertStringToAdapterAttribute(const nsAString& aString);
+
+  /**
+   * Check whether value of given adapter property has changed.
+   *
+   * @param aType  [in] Adapter property to check
+   * @param aValue [in] New value of the adapter property
+   */
+  bool IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
+                                 const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  /**
+   * Current state of this adapter. Can be Disabled/Disabling/Enabled/Enabling.
+   */
+  BluetoothAdapterState mState;
+
+  /**
+   * BD address of this adapter.
+   */
+  nsString mAddress;
+
+  /**
+   * Human-readable name of this adapter.
+   */
+  nsString mName;
+
+  /**
+   * Whether this adapter can be discovered by nearby devices.
+   */
+  bool mDiscoverable;
+
+  /**
+   * Whether this adapter is discovering nearby devices.
+   */
+  bool mDiscovering;
+
+  /**
+   * Handle to fire pairing requests of different pairing types.
+   */
+  nsRefPtr<BluetoothPairingListener> mPairingReqs;
+
+  /**
+   * Handle to fire 'ondevicefound' event handler for discovered device.
+   *
+   * This variable is set to the latest discovery handle when adapter just
+   * starts discovery, and is reset to nullptr when discovery is stopped by
+   * some adapter.
+   */
+  nsRefPtr<BluetoothDiscoveryHandle> mDiscoveryHandleInUse;
+
+  /**
+   * Arrays of references to BluetoothDevices created by this adapter.
+   * This array is empty when adapter state is Disabled.
    *
    * Devices will be appended when
    * 1) Enabling BT: Paired devices reported by stack.
    * 2) Discovering: Discovered devices during discovery operation.
    * A device won't be appended if a device object with the same
    * address already exists.
    *
    * Devices will be removed when
    * 1) Starting discovery: All unpaired devices will be removed before this
    *    adapter starts a new discovery.
    * 2) Disabling BT: All devices will be removed.
    */
   nsTArray<nsRefPtr<BluetoothDevice> > mDevices;
-  nsRefPtr<BluetoothDiscoveryHandle> mDiscoveryHandleInUse;
-  nsRefPtr<BluetoothPairingListener> mPairingReqs;
-  BluetoothAdapterState mState;
-  nsString mAddress;
-  nsString mName;
-  bool mDiscoverable;
-  bool mDiscovering;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth2/BluetoothDevice.cpp
@@ -22,17 +22,17 @@ USING_BLUETOOTH_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice, DOMEventTargetHelper, mCod)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 
-class FetchUuidsTask : public BluetoothReplyRunnable
+class FetchUuidsTask MOZ_FINAL : public BluetoothReplyRunnable
 {
 public:
   FetchUuidsTask(Promise* aPromise,
                  const nsAString& aName,
                  BluetoothDevice* aDevice)
     : BluetoothReplyRunnable(nullptr /* DOMRequest */, aPromise, aName)
     , mDevice(aDevice)
   {
--- a/dom/bluetooth2/BluetoothDevice.h
+++ b/dom/bluetooth2/BluetoothDevice.h
@@ -28,25 +28,22 @@ class BluetoothValue;
 class BluetoothSignal;
 class BluetoothSocket;
 
 class BluetoothDevice MOZ_FINAL : public DOMEventTargetHelper
                                 , public BluetoothSignalObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothDevice,
                                            DOMEventTargetHelper)
 
-  static already_AddRefed<BluetoothDevice>
-  Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
-
-  void Notify(const BluetoothSignal& aParam);
-
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
   void GetAddress(nsString& aAddress) const
   {
     aAddress = mAddress;
   }
 
   BluetoothClassOfDevice* Cod() const
   {
     return mCod;
@@ -57,52 +54,120 @@ public:
     aName = mName;
   }
 
   bool Paired() const
   {
     return mPaired;
   }
 
-  void GetUuids(nsTArray<nsString>& aUuids) {
+  void GetUuids(nsTArray<nsString>& aUuids) const
+  {
     aUuids = mUuids;
   }
 
+  /****************************************************************************
+   * Event Handlers
+   ***************************************************************************/
+  IMPL_EVENT_HANDLER(attributechanged);
+
+  /****************************************************************************
+   * Methods (Web API Implementation)
+   ***************************************************************************/
   already_AddRefed<Promise> FetchUuids(ErrorResult& aRv);
 
-  void SetPropertyByValue(const BluetoothNamedValue& aValue);
-
-  BluetoothDeviceAttribute
-  ConvertStringToDeviceAttribute(const nsAString& aString);
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  static already_AddRefed<BluetoothDevice>
+    Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
 
-  bool
-  IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
-                           const BluetoothValue& aValue);
-
-  void HandlePropertyChanged(const BluetoothValue& aValue);
-
-  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
-
-  IMPL_EVENT_HANDLER(attributechanged);
-
+  void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
   nsPIDOMWindow* GetParentObject() const
   {
      return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
   virtual void DisconnectFromOwner() MOZ_OVERRIDE;
 
+  /**
+   * Override operator== for device comparison
+   */
+  bool operator==(BluetoothDevice& aDevice) const
+  {
+    nsString address;
+    aDevice.GetAddress(address);
+    return mAddress.Equals(address);
+  }
+
 private:
   BluetoothDevice(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
   ~BluetoothDevice();
 
+  /**
+   * Set device properties according to properties array
+   *
+   * @param aValue [in] Properties array to set with
+   */
+  void SetPropertyByValue(const BluetoothNamedValue& aValue);
+
+  /**
+   * Handle "PropertyChanged" bluetooth signal.
+   *
+   * @param aValue [in] Array of changed properties
+   */
+  void HandlePropertyChanged(const BluetoothValue& aValue);
+
+  /**
+   * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
+   */
+  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
+
+  /**
+   * Convert string to BluetoothDeviceAttribute.
+   *
+   * @param aString [in] String to convert
+   */
+  BluetoothDeviceAttribute
+    ConvertStringToDeviceAttribute(const nsAString& aString);
+
+  /**
+   * Check whether value of given device property has changed.
+   *
+   * @param aType  [in] Device property to check
+   * @param aValue [in] New value of the device property
+   */
+  bool IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
+                                const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  /**
+   * BD address of this device.
+   */
   nsString mAddress;
+
+  /**
+   * Class of device (CoD) that describes this device's capabilities.
+   */
   nsRefPtr<BluetoothClassOfDevice> mCod;
+
+  /**
+   * Human-readable name of this device.
+   */
   nsString mName;
+
+  /**
+   * Whether this device is paired or not.
+   */
   bool mPaired;
+
+  /**
+   * Cached UUID list of services which this device provides.
+   */
   nsTArray<nsString> mUuids;
-
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/BluetoothManager.h
+++ b/dom/bluetooth2/BluetoothManager.h
@@ -21,55 +21,64 @@ class BluetoothAdapter;
 class BluetoothValue;
 
 class BluetoothManager : public DOMEventTargetHelper
                        , public BluetoothSignalObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  // Never returns null
-  static already_AddRefed<BluetoothManager> Create(nsPIDOMWindow* aWindow);
-  static bool CheckPermission(nsPIDOMWindow* aWindow);
+  /****************************************************************************
+   * Event Handlers
+   ***************************************************************************/
+  IMPL_EVENT_HANDLER(attributechanged);
+  IMPL_EVENT_HANDLER(adapteradded);
+  IMPL_EVENT_HANDLER(adapterremoved);
 
+  /****************************************************************************
+   * Methods (Web API Implementation)
+   ***************************************************************************/
   /**
    * Return default adapter if it exists, nullptr otherwise. The function is
    * called when applications access property BluetoothManager.defaultAdapter
    */
   BluetoothAdapter* GetDefaultAdapter();
 
   /**
    * Return adapters array. The function is called when applications call
    * method BluetoothManager.getAdapters()
    *
    * @param aAdapters [out] Adapters array to return
    */
   void GetAdapters(nsTArray<nsRefPtr<BluetoothAdapter> >& aAdapters);
 
-  /**
-   * Create a BluetoothAdapter object based on properties array
-   * and append it into adapters array.
-   *
-   * @param aValue [in] Properties array to create BluetoothAdapter object
-   */
-  void AppendAdapter(const BluetoothValue& aValue);
-
-  IMPL_EVENT_HANDLER(attributechanged);
-  IMPL_EVENT_HANDLER(adapteradded);
-  IMPL_EVENT_HANDLER(adapterremoved);
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  // Never returns null
+  static already_AddRefed<BluetoothManager> Create(nsPIDOMWindow* aWindow);
+  static bool CheckPermission(nsPIDOMWindow* aWindow);
 
   void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
   virtual void DisconnectFromOwner() MOZ_OVERRIDE;
 
+  /**
+   * Create a BluetoothAdapter object based on properties array
+   * and append it into adapters array.
+   *
+   * @param aValue [in] Properties array to create BluetoothAdapter object
+   */
+  void AppendAdapter(const BluetoothValue& aValue);
+
 private:
   BluetoothManager(nsPIDOMWindow* aWindow);
   ~BluetoothManager();
 
   /**
    * Start/Stop listening to bluetooth signal.
    *
    * @param aStart [in] Whether to start or stop listening to bluetooth signal
--- a/dom/bluetooth2/BluetoothRilListener.cpp
+++ b/dom/bluetooth2/BluetoothRilListener.cpp
@@ -108,22 +108,22 @@ MobileConnectionListener::NotifyUssdRece
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyDataError(const nsAString & message)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileConnectionListener::NotifyCFStateChange(bool success,
-                                              uint16_t action,
-                                              uint16_t reason,
-                                              const nsAString& number,
-                                              uint16_t timeSeconds,
-                                              uint16_t serviceClass)
+MobileConnectionListener::NotifyCFStateChanged(bool success,
+                                               uint16_t action,
+                                               uint16_t reason,
+                                               const nsAString& number,
+                                               uint16_t timeSeconds,
+                                               uint16_t serviceClass)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyEmergencyCbModeChanged(bool active,
                                                        uint32_t timeoutMs)
 {
@@ -149,28 +149,46 @@ MobileConnectionListener::NotifyRadioSta
 }
 
 NS_IMETHODIMP
 MobileConnectionListener::NotifyClirModeChanged(uint32_t aMode)
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MobileConnectionListener::NotifyLastKnownNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionListener::NotifyLastKnownHomeNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionListener::NotifyNetworkSelectionModeChanged()
+{
+  return NS_OK;
+}
+
 bool
 MobileConnectionListener::Listen(bool aStart)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE(provider, false);
+  nsCOMPtr<nsIMobileConnectionService> service =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(service, false);
 
   nsresult rv;
   if (aStart) {
-    rv = provider->RegisterMobileConnectionMsg(mClientId, this);
+    rv = service->RegisterListener(mClientId, this);
   } else {
-    rv = provider->UnregisterMobileConnectionMsg(mClientId, this);
+    rv = service->UnregisterListener(mClientId, this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 /**
  *  TelephonyListener Implementation
  */
@@ -347,23 +365,23 @@ BluetoothRilListener::Listen(bool aStart
 }
 
 void
 BluetoothRilListener::SelectClient()
 {
   // Reset mClientId
   mClientId = mMobileConnListeners.Length();
 
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(connection);
+  nsCOMPtr<nsIMobileConnectionService> service =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
 
   for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
     nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
-    connection->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
+    service->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
     if (!voiceInfo) {
       BT_WARNING("%s: Failed to get voice connection info", __FUNCTION__);
       continue;
     }
 
     nsString regState;
     voiceInfo->GetState(regState);
     if (regState.EqualsLiteral("registered")) {
--- a/dom/bluetooth2/BluetoothRilListener.h
+++ b/dom/bluetooth2/BluetoothRilListener.h
@@ -7,17 +7,17 @@
 #ifndef mozilla_dom_bluetooth_bluetoothrillistener_h__
 #define mozilla_dom_bluetooth_bluetoothrillistener_h__
 
 #include "BluetoothCommon.h"
 
 #include "nsAutoPtr.h"
 
 #include "nsIIccProvider.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsITelephonyService.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothRilListener;
 
 class IccListener : public nsIIccListener
 {
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
@@ -610,18 +610,18 @@ BluetoothHfpManager::HandleVolumeChanged
     sBluetoothHfpInterface->VolumeControl(HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs,
                                           new VolumeControlResultHandler());
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIMobileConnectionService> connection =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
   connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -23,17 +23,17 @@
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #endif
 
 /**
  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
  * Capabilities" in Bluetooth hands-free profile 1.6
@@ -601,18 +601,18 @@ BluetoothHfpManager::HandleVolumeChanged
     SendCommand(RESPONSE_VGS, mCurrentVgs);
   }
 }
 
 #ifdef MOZ_B2G_RIL
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIMobileConnectionService> connection =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
   connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -149,16 +149,21 @@
 #include "AudioChannelService.h"
 #include "JavaScriptChild.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 
+#ifdef MOZ_B2G_RIL
+#include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
+using namespace mozilla::dom::mobileconnection;
+#endif
+
 using namespace base;
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::indexedDB;
@@ -1217,16 +1222,53 @@ ContentChild::DeallocPFileSystemRequestC
     mozilla::dom::FileSystemTaskBase* child =
       static_cast<mozilla::dom::FileSystemTaskBase*>(aFileSystem);
     // The reference is increased in FileSystemTaskBase::Start of
     // FileSystemTaskBase.cpp. We should decrease it after IPC.
     NS_RELEASE(child);
     return true;
 }
 
+PMobileConnectionChild*
+ContentChild::SendPMobileConnectionConstructor(PMobileConnectionChild* aActor,
+                                               const uint32_t& aClientId)
+{
+#ifdef MOZ_B2G_RIL
+    // Add an extra ref for IPDL. Will be released in
+    // ContentChild::DeallocPMobileConnectionChild().
+    static_cast<MobileConnectionChild*>(aActor)->AddRef();
+    return PContentChild::SendPMobileConnectionConstructor(aActor, aClientId);
+#else
+    MOZ_CRASH("No support for mobileconnection on this platform!");;
+#endif
+}
+
+PMobileConnectionChild*
+ContentChild::AllocPMobileConnectionChild(const uint32_t& aClientId)
+{
+#ifdef MOZ_B2G_RIL
+    NS_NOTREACHED("No one should be allocating PMobileConnectionChild actors");
+    return nullptr;
+#else
+    MOZ_CRASH("No support for mobileconnection on this platform!");;
+#endif
+}
+
+bool
+ContentChild::DeallocPMobileConnectionChild(PMobileConnectionChild* aActor)
+{
+#ifdef MOZ_B2G_RIL
+    // MobileConnectionChild is refcounted, must not be freed manually.
+    static_cast<MobileConnectionChild*>(aActor)->Release();
+    return true;
+#else
+    MOZ_CRASH("No support for mobileconnection on this platform!");
+#endif
+}
+
 PNeckoChild*
 ContentChild::AllocPNeckoChild()
 {
     return new NeckoChild();
 }
 
 bool
 ContentChild::DeallocPNeckoChild(PNeckoChild* necko)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -186,16 +186,24 @@ public:
     RecvDataStoreNotify(const uint32_t& aAppId, const nsString& aName,
                         const nsString& aManifestURL) MOZ_OVERRIDE;
 
     virtual PTestShellChild* AllocPTestShellChild() MOZ_OVERRIDE;
     virtual bool DeallocPTestShellChild(PTestShellChild*) MOZ_OVERRIDE;
     virtual bool RecvPTestShellConstructor(PTestShellChild*) MOZ_OVERRIDE;
     jsipc::JavaScriptChild *GetCPOWManager();
 
+    PMobileConnectionChild*
+    SendPMobileConnectionConstructor(PMobileConnectionChild* aActor,
+                                     const uint32_t& aClientId);
+    virtual PMobileConnectionChild*
+    AllocPMobileConnectionChild(const uint32_t& aClientId) MOZ_OVERRIDE;
+    virtual bool
+    DeallocPMobileConnectionChild(PMobileConnectionChild* aActor) MOZ_OVERRIDE;
+
     virtual PNeckoChild* AllocPNeckoChild() MOZ_OVERRIDE;
     virtual bool DeallocPNeckoChild(PNeckoChild*) MOZ_OVERRIDE;
 
     virtual PScreenManagerChild*
     AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
                              float* aSystemDefaultScale,
                              bool* aSuccess) MOZ_OVERRIDE;
     virtual bool DeallocPScreenManagerChild(PScreenManagerChild*) MOZ_OVERRIDE;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -172,16 +172,21 @@ using namespace mozilla::system;
 #endif
 
 #ifdef ENABLE_TESTS
 #include "BackgroundChildImpl.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #endif
 
+#ifdef MOZ_B2G_RIL
+#include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
+using namespace mozilla::dom::mobileconnection;
+#endif
+
 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #endif
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
@@ -3089,16 +3094,42 @@ ContentParent::AllocPTestShellParent()
 
 bool
 ContentParent::DeallocPTestShellParent(PTestShellParent* shell)
 {
     delete shell;
     return true;
 }
 
+PMobileConnectionParent*
+ContentParent::AllocPMobileConnectionParent(const uint32_t& aClientId)
+{
+#ifdef MOZ_B2G_RIL
+    nsRefPtr<MobileConnectionParent> parent = new MobileConnectionParent(aClientId);
+    // We release this ref in DeallocPMobileConnectionParent().
+    parent->AddRef();
+
+    return parent;
+#else
+    MOZ_CRASH("No support for mobileconnection on this platform!");
+#endif
+}
+
+bool
+ContentParent::DeallocPMobileConnectionParent(PMobileConnectionParent* aActor)
+{
+#ifdef MOZ_B2G_RIL
+    // MobileConnectionParent is refcounted, must not be freed manually.
+    static_cast<MobileConnectionParent*>(aActor)->Release();
+    return true;
+#else
+    MOZ_CRASH("No support for mobileconnection on this platform!");
+#endif
+}
+
 PNeckoParent*
 ContentParent::AllocPNeckoParent()
 {
     return new NeckoParent();
 }
 
 bool
 ContentParent::DeallocPNeckoParent(PNeckoParent* necko)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -458,16 +458,19 @@ private:
                                      const FileDescriptor& aGCLog,
                                      const FileDescriptor& aCCLog) MOZ_OVERRIDE;
     virtual bool
     DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) MOZ_OVERRIDE;
 
     virtual PTestShellParent* AllocPTestShellParent() MOZ_OVERRIDE;
     virtual bool DeallocPTestShellParent(PTestShellParent* shell) MOZ_OVERRIDE;
 
+    virtual PMobileConnectionParent* AllocPMobileConnectionParent(const uint32_t& aClientId) MOZ_OVERRIDE;
+    virtual bool DeallocPMobileConnectionParent(PMobileConnectionParent* aActor) MOZ_OVERRIDE;
+
     virtual bool DeallocPNeckoParent(PNeckoParent* necko) MOZ_OVERRIDE;
 
     virtual PExternalHelperAppParent* AllocPExternalHelperAppParent(
             const OptionalURIParams& aUri,
             const nsCString& aMimeContentType,
             const nsCString& aContentDisposition,
             const uint32_t& aContentDispositionHint,
             const nsString& aContentDispositionFilename, 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -17,16 +17,17 @@ include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
+include protocol PMobileConnection;
 include protocol PNecko;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
@@ -305,16 +306,17 @@ intr protocol PContent
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PIndexedDB;
     manages PMemoryReportRequest;
+    manages PMobileConnection;
     manages PNecko;
     manages PScreenManager;
     manages PSms;
     manages PSpeechSynthesis;
     manages PStorage;
     manages PTelephony;
     manages PTestShell;
     manages PJavaScript;
@@ -493,16 +495,18 @@ parent:
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
     PHal();
 
     PIndexedDB();
 
+    PMobileConnection(uint32_t clientId);
+
     PNecko();
 
     rpc PScreenManager()
         returns (uint32_t numberOfScreens,
                  float systemDefaultScale,
                  bool success);
 
     PSms();
--- a/dom/mobileconnection/DOMMMIError.cpp
+++ b/dom/mobileconnection/DOMMMIError.cpp
@@ -1,13 +1,13 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this file,
-* You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * 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 "DOMMMIError.h"
 #include "mozilla/dom/DOMMMIErrorBinding.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMMIError)
 
@@ -32,24 +32,8 @@ DOMMMIError::DOMMMIError(nsPIDOMWindow* 
 {
 }
 
 JSObject*
 DOMMMIError::WrapObject(JSContext* aCx)
 {
   return DOMMMIErrorBinding::Wrap(aCx, this);
 }
-
-// WebIDL interface
-
-/* static */ already_AddRefed<DOMMMIError>
-DOMMMIError::Constructor(const GlobalObject& aGlobal,
-                         const nsAString& aServiceCode,
-                         const nsAString& aName,
-                         const nsAString& aMessage,
-                         const Nullable<int16_t>& aInfo,
-                         ErrorResult& aRv) {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  nsRefPtr<DOMMMIError> error = new DOMMMIError(window, aName, aMessage,
-                                                aServiceCode, aInfo);
-
-  return error.forget();
-}
--- a/dom/mobileconnection/DOMMMIError.h
+++ b/dom/mobileconnection/DOMMMIError.h
@@ -1,13 +1,13 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this file,
-* You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * 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_MmiError_h
 #define mozilla_dom_MmiError_h
 
 #include "mozilla/dom/DOMError.h"
 
 namespace mozilla {
 namespace dom {
@@ -21,20 +21,16 @@ public:
   DOMMMIError(nsPIDOMWindow* aWindow, const nsAString& aName,
               const nsAString& aMessage, const nsAString& aServiceCode,
               const Nullable<int16_t>& aInfo);
 
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL interface
-  static already_AddRefed<DOMMMIError>
-  Constructor(const GlobalObject& aGlobal, const nsAString& aServiceCode,
-              const nsAString& aName, const nsAString& aMessage,
-              const Nullable<int16_t>& aInfo, ErrorResult& aRv);
 
   void
   GetServiceCode(nsString& aServiceCode) const
   {
     aServiceCode = mServiceCode;
   }
 
   Nullable<int16_t>
--- a/dom/mobileconnection/MobileCellInfo.cpp
+++ b/dom/mobileconnection/MobileCellInfo.cpp
@@ -12,31 +12,53 @@ using namespace mozilla::dom;
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MobileCellInfo, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MobileCellInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MobileCellInfo)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MobileCellInfo)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIMobileCellInfo)
 NS_INTERFACE_MAP_END
 
 MobileCellInfo::MobileCellInfo(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
   , mGsmLocationAreaCode(-1)
   , mGsmCellId(-1)
   , mCdmaBaseStationId(-1)
   , mCdmaBaseStationLatitude(-1)
   , mCdmaBaseStationLongitude(-1)
   , mCdmaSystemId(-1)
   , mCdmaNetworkId(-1)
 {
   SetIsDOMBinding();
 }
 
+MobileCellInfo::MobileCellInfo(int32_t aGsmLocationAreaCode,
+                               int64_t aGsmCellId,
+                               int32_t aCdmaBaseStationId,
+                               int32_t aCdmaBaseStationLatitude,
+                               int32_t aCdmaBaseStationLongitude,
+                               int32_t aCdmaSystemId,
+                               int32_t aCdmaNetworkId)
+  : mGsmLocationAreaCode(aGsmLocationAreaCode)
+  , mGsmCellId(aGsmCellId)
+  , mCdmaBaseStationId(aCdmaBaseStationId)
+  , mCdmaBaseStationLatitude(aCdmaBaseStationLatitude)
+  , mCdmaBaseStationLongitude(aCdmaBaseStationLongitude)
+  , mCdmaSystemId(aCdmaSystemId)
+  , mCdmaNetworkId(aCdmaNetworkId)
+{
+  // The instance created by this way is only used for IPC stuff. It won't be
+  // expose to JS directly, we will clone this instance to the one that is
+  // maintained in MobileConnectionChild. So we don't need SetIsDOMBinding()
+  // here.
+}
+
 void
 MobileCellInfo::Update(nsIMobileCellInfo* aInfo)
 {
   if (!aInfo) {
     return;
   }
 
   aInfo->GetGsmLocationAreaCode(&mGsmLocationAreaCode);
@@ -46,10 +68,62 @@ MobileCellInfo::Update(nsIMobileCellInfo
   aInfo->GetCdmaBaseStationLongitude(&mCdmaBaseStationLongitude);
   aInfo->GetCdmaSystemId(&mCdmaSystemId);
   aInfo->GetCdmaNetworkId(&mCdmaNetworkId);
 }
 
 JSObject*
 MobileCellInfo::WrapObject(JSContext* aCx)
 {
+  MOZ_ASSERT(IsDOMBinding());
   return MozMobileCellInfoBinding::Wrap(aCx, this);
 }
+
+// nsIMobileCellInfo
+
+NS_IMETHODIMP
+MobileCellInfo::GetGsmLocationAreaCode(int32_t* aGsmLocationAreaCode)
+{
+  *aGsmLocationAreaCode = GsmLocationAreaCode();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetGsmCellId(int64_t* aGsmCellId)
+{
+  *aGsmCellId = GsmCellId();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetCdmaBaseStationId(int32_t* aCdmaBaseStationId)
+{
+  *aCdmaBaseStationId = CdmaBaseStationId();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetCdmaBaseStationLatitude(int32_t* aCdmaBaseStationLatitude)
+{
+  *aCdmaBaseStationLatitude = CdmaBaseStationLatitude();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetCdmaBaseStationLongitude(int32_t* aCdmaBaseStationLongitude)
+{
+  *aCdmaBaseStationLongitude = CdmaBaseStationLongitude();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetCdmaSystemId(int32_t* aCdmaSystemId)
+{
+  *aCdmaSystemId = CdmaSystemId();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileCellInfo::GetCdmaNetworkId(int32_t* aCdmaNetworkId)
+{
+  *aCdmaNetworkId = CdmaNetworkId();
+  return NS_OK;
+}
--- a/dom/mobileconnection/MobileCellInfo.h
+++ b/dom/mobileconnection/MobileCellInfo.h
@@ -9,25 +9,31 @@
 
 #include "nsIMobileCellInfo.h"
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
-class MobileCellInfo MOZ_FINAL : public nsISupports
+class MobileCellInfo MOZ_FINAL : public nsIMobileCellInfo
                                , public nsWrapperCache
 {
 public:
+  NS_DECL_NSIMOBILECELLINFO
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MobileCellInfo)
 
   MobileCellInfo(nsPIDOMWindow* aWindow);
 
+  MobileCellInfo(int32_t aGsmLocationAreaCode, int64_t aGsmCellId,
+                 int32_t aCdmaBaseStationId, int32_t aCdmaBaseStationLatitude,
+                 int32_t aCdmaBaseStationLongitude, int32_t aCdmaSystemId,
+                 int32_t aCdmaNetworkId);
+
   void
   Update(nsIMobileCellInfo* aInfo);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
--- a/dom/mobileconnection/MobileConnection.cpp
+++ b/dom/mobileconnection/MobileConnection.cpp
@@ -1,32 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MobileConnection.h"
 
+#include "MobileConnectionCallback.h"
 #include "mozilla/dom/CFStateChangeEvent.h"
 #include "mozilla/dom/DataErrorEvent.h"
 #include "mozilla/dom/MozClirModeEvent.h"
 #include "mozilla/dom/MozEmergencyCbModeEvent.h"
 #include "mozilla/dom/MozOtaStatusEvent.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/USSDReceivedEvent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIPermissionManager.h"
 #include "nsIVariant.h"
 #include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 
-#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
-
 #define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum)      \
 {                                                                       \
   uint32_t i = 0;                                                       \
   for (const EnumEntry* entry = _enumType##Values::strings;             \
        entry->value;                                                    \
        ++entry, ++i) {                                                  \
     if (_string.EqualsASCII(entry->value)) {                            \
       _enum.SetValue(static_cast<_enumType>(i));                        \
@@ -36,16 +35,17 @@
 
 #define CONVERT_ENUM_TO_STRING(_enumType, _enum, _string)               \
 {                                                                       \
   uint32_t index = uint32_t(_enum);                                     \
   _string.AssignASCII(_enumType##Values::strings[index].value,          \
                       _enumType##Values::strings[index].length);        \
 }
 
+using mozilla::ErrorResult;
 using namespace mozilla::dom;
 
 class MobileConnection::Listener MOZ_FINAL : public nsIMobileConnectionListener
 {
   MobileConnection* mMobileConnection;
 
 public:
   NS_DECL_ISUPPORTS
@@ -72,17 +72,17 @@ private:
 
 NS_IMPL_ISUPPORTS(MobileConnection::Listener, nsIMobileConnectionListener)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MobileConnection)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MobileConnection,
                                                   DOMEventTargetHelper)
   // Don't traverse mListener because it doesn't keep any reference to
-  // MobileConnection but a raw pointer instead. Neither does mProvider because
+  // MobileConnection but a raw pointer instead. Neither does mService because
   // it's an xpcom service and is only released at shutting down.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoice)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MobileConnection,
                                                 DOMEventTargetHelper)
   tmp->Shutdown();
@@ -100,44 +100,44 @@ NS_IMPL_ADDREF_INHERITED(MobileConnectio
 NS_IMPL_RELEASE_INHERITED(MobileConnection, DOMEventTargetHelper)
 
 MobileConnection::MobileConnection(nsPIDOMWindow* aWindow, uint32_t aClientId)
   : DOMEventTargetHelper(aWindow)
   , mClientId(aClientId)
 {
   SetIsDOMBinding();
 
-  mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  mService = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
 
-  // Not being able to acquire the provider isn't fatal since we check
+  // Not being able to acquire the service isn't fatal since we check
   // for it explicitly below.
-  if (!mProvider) {
-    NS_WARNING("Could not acquire nsIMobileConnectionProvider!");
+  if (!mService) {
+    NS_WARNING("Could not acquire nsIMobileConnectionService!");
     return;
   }
 
   mListener = new Listener(this);
   mVoice = new MobileConnectionInfo(GetOwner());
   mData = new MobileConnectionInfo(GetOwner());
 
   if (CheckPermission("mobileconnection")) {
-    DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mClientId, mListener);
+    DebugOnly<nsresult> rv = mService->RegisterListener(mClientId, mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                     "Failed registering mobile connection messages with provider");
+                     "Failed registering mobile connection messages with service");
     UpdateVoice();
     UpdateData();
   }
 }
 
 void
 MobileConnection::Shutdown()
 {
   if (mListener) {
-    if (mProvider) {
-      mProvider->UnregisterMobileConnectionMsg(mClientId, mListener);
+    if (mService) {
+      mService->UnregisterListener(mClientId, mListener);
     }
 
     mListener->Disconnect();
     mListener = nullptr;
   }
 }
 
 MobileConnection::~MobileConnection()
@@ -170,61 +170,61 @@ MobileConnection::CheckPermission(const 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
   permMgr->TestPermissionFromWindow(GetOwner(), aType, &permission);
   return permission == nsIPermissionManager::ALLOW_ACTION;
 }
 
 void
 MobileConnection::UpdateVoice()
 {
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  mProvider->GetVoiceConnectionInfo(mClientId, getter_AddRefs(info));
+  mService->GetVoiceConnectionInfo(mClientId, getter_AddRefs(info));
   mVoice->Update(info);
 }
 
 void
 MobileConnection::UpdateData()
 {
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
   nsCOMPtr<nsIMobileConnectionInfo> info;
-  mProvider->GetDataConnectionInfo(mClientId, getter_AddRefs(info));
+  mService->GetDataConnectionInfo(mClientId, getter_AddRefs(info));
   mData->Update(info);
 }
 
 // WebIDL interface
 
 void
 MobileConnection::GetLastKnownNetwork(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
-  mProvider->GetLastKnownNetwork(mClientId, aRetVal);
+  mService->GetLastKnownNetwork(mClientId, aRetVal);
 }
 
 void
 MobileConnection::GetLastKnownHomeNetwork(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
-  mProvider->GetLastKnownHomeNetwork(mClientId, aRetVal);
+  mService->GetLastKnownHomeNetwork(mClientId, aRetVal);
 }
 
 // All fields below require the "mobileconnection" permission.
 
 MobileConnectionInfo*
 MobileConnection::Voice() const
 {
   return mVoice;
@@ -236,66 +236,66 @@ MobileConnection::Data() const
   return mData;
 }
 
 void
 MobileConnection::GetIccId(nsString& aRetVal) const
 {
   aRetVal.SetIsVoid(true);
 
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
-  mProvider->GetIccId(mClientId, aRetVal);
+  mService->GetIccId(mClientId, aRetVal);
 }
 
 Nullable<MobileNetworkSelectionMode>
 MobileConnection::GetNetworkSelectionMode() const
 {
   Nullable<MobileNetworkSelectionMode> retVal =
     Nullable<MobileNetworkSelectionMode>();
 
-  if (!mProvider) {
+  if (!mService) {
     return retVal;
   }
 
   nsAutoString mode;
-  mProvider->GetNetworkSelectionMode(mClientId, mode);
+  mService->GetNetworkSelectionMode(mClientId, mode);
   CONVERT_STRING_TO_NULLABLE_ENUM(mode, MobileNetworkSelectionMode, retVal);
 
   return retVal;
 }
 
 Nullable<MobileRadioState>
 MobileConnection::GetRadioState() const
 {
   Nullable<MobileRadioState> retVal = Nullable<MobileRadioState>();
 
-  if (!mProvider) {
+  if (!mService) {
     return retVal;
   }
 
   nsAutoString state;
-  mProvider->GetRadioState(mClientId, state);
+  mService->GetRadioState(mClientId, state);
   CONVERT_STRING_TO_NULLABLE_ENUM(state, MobileRadioState, retVal);
 
   return retVal;
 }
 
 void
 MobileConnection::GetSupportedNetworkTypes(nsTArray<MobileNetworkType>& aTypes) const
 {
-  if (!mProvider) {
+  if (!mService) {
     return;
   }
 
   nsCOMPtr<nsIVariant> variant;
-  mProvider->GetSupportedNetworkTypes(mClientId,
-                                      getter_AddRefs(variant));
+  mService->GetSupportedNetworkTypes(mClientId,
+                                     getter_AddRefs(variant));
 
   uint16_t type;
   nsIID iid;
   uint32_t count;
   void* data;
 
   // Convert the nsIVariant to an array.  We own the resulting buffer and its
   // elements.
@@ -317,253 +317,281 @@ MobileConnection::GetSupportedNetworkTyp
     }
   }
   NS_Free(data);
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetNetworks(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetNetworks(mClientId, GetOwner(),
-                                       getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetNetworks(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SelectNetwork(MobileNetworkInfo& aNetwork, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SelectNetwork(mClientId, GetOwner(), &aNetwork,
-                                         getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SelectNetwork(mClientId, &aNetwork, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SelectNetworkAutomatically(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SelectNetworkAutomatically(mClientId, GetOwner(),
-                                                      getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SelectNetworkAutomatically(mClientId,
+                                                     requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetPreferredNetworkType(MobilePreferredNetworkType& aType,
                                           ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsAutoString type;
   CONVERT_ENUM_TO_STRING(MobilePreferredNetworkType, aType, type);
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetPreferredNetworkType(mClientId, GetOwner(), type,
-                                                   getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetPreferredNetworkType(mClientId, type,
+                                                  requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetPreferredNetworkType(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetPreferredNetworkType(mClientId, GetOwner(),
-                                                   getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetPreferredNetworkType(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetRoamingPreference(MobileRoamingMode& aMode,
                                        ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsAutoString mode;
   CONVERT_ENUM_TO_STRING(MobileRoamingMode, aMode, mode);
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetRoamingPreference(mClientId, GetOwner(), mode,
-                                                getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetRoamingPreference(mClientId, mode, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetRoamingPreference(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetRoamingPreference(mClientId, GetOwner(),
-                                                getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetRoamingPreference(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
+    return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetVoicePrivacyMode(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetVoicePrivacyMode(mClientId, GetOwner(), aEnabled,
-                                               getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetVoicePrivacyMode(mClientId, aEnabled,
+                                              requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetVoicePrivacyMode(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetVoicePrivacyMode(mClientId, GetOwner(),
-                                               getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetVoicePrivacyMode(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SendMMI(const nsAString& aMMIString, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SendMMI(mClientId, GetOwner(), aMMIString,
-                                   getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SendMMI(mClientId, aMMIString, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::CancelMMI(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->CancelMMI(mClientId, GetOwner(),
-                                     getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->CancelMMI(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallForwardingOption(uint16_t aReason, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCallForwarding(mClientId, GetOwner(), aReason,
-                                             getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetCallForwarding(mClientId, aReason, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallForwardingOption(const MozCallForwardingOptions& aOptions,
                                           ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -571,32 +599,34 @@ MobileConnection::SetCallForwardingOptio
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetCallForwarding(mClientId, GetOwner(), options,
-                                             getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetCallForwarding(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallBarringOption(const MozCallBarringOptions& aOptions,
                                        ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -604,32 +634,34 @@ MobileConnection::GetCallBarringOption(c
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCallBarring(mClientId, GetOwner(), options,
-                                          getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetCallBarring(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallBarringOption(const MozCallBarringOptions& aOptions,
                                        ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -637,32 +669,34 @@ MobileConnection::SetCallBarringOption(c
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetCallBarring(mClientId, GetOwner(), options,
-                                          getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetCallBarring(mClientId, options, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::ChangeCallBarringPassword(const MozCallBarringOptions& aOptions,
                                             ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
   if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -670,144 +704,157 @@ MobileConnection::ChangeCallBarringPassw
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->ChangeCallBarringPassword(mClientId,
-                                                     GetOwner(),
-                                                     options,
-                                                     getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->ChangeCallBarringPassword(mClientId, options,
+                                                    requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallWaitingOption(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCallWaiting(mClientId, GetOwner(),
-                                          getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetCallWaiting(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallWaitingOption(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetCallWaiting(mClientId, GetOwner(), aEnabled,
-                                          getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetCallWaiting(mClientId, aEnabled, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::GetCallingLineIdRestriction(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCallingLineIdRestriction(mClientId, GetOwner(),
-                                                       getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->GetCallingLineIdRestriction(mClientId,
+                                                      requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetCallingLineIdRestriction(uint16_t aMode,
                                               ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetCallingLineIdRestriction(mClientId,
-                                                       GetOwner(),
-                                                       aMode,
-                                                       getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetCallingLineIdRestriction(mClientId, aMode,
+                                                      requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::ExitEmergencyCbMode(ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->ExitEmergencyCbMode(mClientId, GetOwner(),
-                                               getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->ExitEmergencyCbMode(mClientId, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 MobileConnection::SetRadioEnabled(bool aEnabled, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mService) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->SetRadioEnabled(mClientId, GetOwner(), aEnabled,
-                                           getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<MobileConnectionCallback> requestCallback =
+    new MobileConnectionCallback(GetOwner(), request);
+
+  nsresult rv = mService->SetRadioEnabled(mClientId, aEnabled, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 // nsIMobileConnectionListener
 
 NS_IMETHODIMP
 MobileConnection::NotifyVoiceChanged()
 {
   if (!CheckPermission("mobileconnection")) {
@@ -865,22 +912,22 @@ MobileConnection::NotifyDataError(const 
 
   nsRefPtr<DataErrorEvent> event =
     DataErrorEvent::Constructor(this, NS_LITERAL_STRING("dataerror"), init);
 
   return DispatchTrustedEvent(event);
 }
 
 NS_IMETHODIMP
-MobileConnection::NotifyCFStateChange(bool aSuccess,
-                                      unsigned short aAction,
-                                      unsigned short aReason,
-                                      const nsAString& aNumber,
-                                      unsigned short aSeconds,
-                                      unsigned short aServiceClass)
+MobileConnection::NotifyCFStateChanged(bool aSuccess,
+                                       unsigned short aAction,
+                                       unsigned short aReason,
+                                       const nsAString& aNumber,
+                                       unsigned short aSeconds,
+                                       unsigned short aServiceClass)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   CFStateChangeEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
@@ -967,8 +1014,26 @@ MobileConnection::NotifyClirModeChanged(
   init.mCancelable = false;
   init.mMode = aMode;
 
   nsRefPtr<MozClirModeEvent> event =
     MozClirModeEvent::Constructor(this, NS_LITERAL_STRING("clirmodechange"), init);
 
   return DispatchTrustedEvent(event);
 }
+
+NS_IMETHODIMP
+MobileConnection::NotifyLastKnownNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnection::NotifyLastKnownHomeNetworkChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnection::NotifyNetworkSelectionModeChanged()
+{
+  return NS_OK;
+}
--- a/dom/mobileconnection/MobileConnection.h
+++ b/dom/mobileconnection/MobileConnection.h
@@ -6,30 +6,30 @@
 #define mozilla_dom_MobileConnection_h
 
 #include "MobileConnectionInfo.h"
 #include "MobileNetworkInfo.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIMobileConnectionProvider.h"
+#include "nsIMobileConnectionService.h"
 #include "nsWeakPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class MobileConnection MOZ_FINAL : public DOMEventTargetHelper,
                                    private nsIMobileConnectionListener
 {
   /**
    * Class MobileConnection doesn't actually expose
    * nsIMobileConnectionListener. Instead, it owns an
    * nsIMobileConnectionListener derived instance mListener and passes it to
-   * nsIMobileConnectionProvider. The onreceived events are first delivered to
+   * nsIMobileConnectionService. The onreceived events are first delivered to
    * mListener and then forwarded to its owner, MobileConnection. See also bug
    * 775997 comment #51.
    */
   class Listener;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIMOBILECONNECTIONLISTENER
@@ -159,17 +159,17 @@ public:
   IMPL_EVENT_HANDLER(radiostatechange)
   IMPL_EVENT_HANDLER(clirmodechange)
 
 private:
   ~MobileConnection();
 
 private:
   uint32_t mClientId;
-  nsCOMPtr<nsIMobileConnectionProvider> mProvider;
+  nsCOMPtr<nsIMobileConnectionService> mService;
   nsRefPtr<Listener> mListener;
   nsRefPtr<MobileConnectionInfo> mVoice;
   nsRefPtr<MobileConnectionInfo> mData;
 
   bool
   CheckPermission(const char* aType) const;
 
   void
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/MobileConnectionCallback.cpp
@@ -0,0 +1,319 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MobileConnectionCallback.h"
+
+#include "DOMMMIError.h"
+#include "mozilla/dom/MobileNetworkInfo.h"
+#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsJSUtils.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(MobileConnectionCallback, nsIMobileConnectionCallback)
+
+MobileConnectionCallback::MobileConnectionCallback(nsPIDOMWindow* aWindow,
+                                                   DOMRequest* aRequest)
+  : mWindow(aWindow)
+  , mRequest(aRequest)
+{
+}
+
+/**
+ * Notify Success for Send/CancelMmi.
+ */
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                                                     const nsAString& aStatusMessage)
+{
+  MozMMIResult result;
+  result.mServiceCode.Assign(aServiceCode);
+  result.mStatusMessage.Assign(aStatusMessage);
+
+  return NotifySendCancelMmiSuccess(result);
+}
+
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                                                     const nsAString& aStatusMessage,
+                                                     JS::Handle<JS::Value> aAdditionalInformation)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  RootedDictionary<MozMMIResult> result(cx);
+
+  result.mServiceCode.Assign(aServiceCode);
+  result.mStatusMessage.Assign(aStatusMessage);
+  result.mAdditionalInformation.Construct().SetAsObject() = &aAdditionalInformation.toObject();
+
+  return NotifySendCancelMmiSuccess(result);
+}
+
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                                                     const nsAString& aStatusMessage,
+                                                     uint16_t aAdditionalInformation)
+{
+  MozMMIResult result;
+  result.mServiceCode.Assign(aServiceCode);
+  result.mStatusMessage.Assign(aStatusMessage);
+  result.mAdditionalInformation.Construct().SetAsUnsignedShort() = aAdditionalInformation;
+
+  return NotifySendCancelMmiSuccess(result);
+}
+
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                                                     const nsAString& aStatusMessage,
+                                                     const nsTArray<nsString>& aAdditionalInformation)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> additionalInformation(cx);
+
+  if (!ToJSValue(cx, aAdditionalInformation, &additionalInformation)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySendCancelMmiSuccess(aServiceCode, aStatusMessage,
+                                    additionalInformation);
+}
+
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                                                     const nsAString& aStatusMessage,
+                                                     const nsTArray<IPC::MozCallForwardingOptions>& aAdditionalInformation)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> additionalInformation(cx);
+
+  if (!ToJSValue(cx, aAdditionalInformation, &additionalInformation)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySendCancelMmiSuccess(aServiceCode, aStatusMessage,
+                                    additionalInformation);
+}
+
+nsresult
+MobileConnectionCallback::NotifySendCancelMmiSuccess(const MozMMIResult& aResult)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+
+  if (!ToJSValue(cx, aResult, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+/**
+ * Notify Success for GetCallForwarding.
+ */
+nsresult
+MobileConnectionCallback::NotifyGetCallForwardingSuccess(const nsTArray<IPC::MozCallForwardingOptions>& aResults)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+
+  if (!ToJSValue(cx, aResults, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+/**
+ * Notify Success.
+ */
+nsresult
+MobileConnectionCallback::NotifySuccess(JS::Handle<JS::Value> aResult)
+{
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireSuccessAsync(mRequest, aResult);
+}
+
+// nsIMobileConnectionCallback
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifySuccess()
+{
+  return NotifySuccess(JS::UndefinedHandleValue);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifySuccessWithString(const nsAString& aResult)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+
+  if (!ToJSValue(cx, aResult, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifySuccessWithBoolean(bool aResult)
+{
+  return aResult ? NotifySuccess(JS::TrueHandleValue)
+                 : NotifySuccess(JS::FalseHandleValue);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifyGetNetworksSuccess(uint32_t aCount,
+                                                   nsIMobileNetworkInfo** aNetworks)
+{
+  nsTArray<nsRefPtr<MobileNetworkInfo>> results;
+  for (uint32_t i = 0; i < aCount; i++)
+  {
+    nsRefPtr<MobileNetworkInfo> networkInfo = new MobileNetworkInfo(mWindow);
+    networkInfo->Update(aNetworks[i]);
+    results.AppendElement(networkInfo);
+  }
+
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+
+  if (!ToJSValue(cx, results, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifySendCancelMmiSuccess(JS::Handle<JS::Value> aResult)
+{
+  return NotifySuccess(aResult);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifyGetCallForwardingSuccess(JS::Handle<JS::Value> aResults)
+{
+  return NotifySuccess(aResults);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifyGetCallBarringSuccess(uint16_t aProgram,
+                                                      bool aEnabled,
+                                                      uint16_t aServiceClass)
+{
+  MozCallBarringOptions result;
+  result.mProgram.Construct().SetValue(aProgram);
+  result.mEnabled.Construct().SetValue(aEnabled);
+  result.mServiceClass.Construct().SetValue(aServiceClass);
+
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+  if (!ToJSValue(cx, result, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifyGetClirStatusSuccess(uint16_t aN, uint16_t aM)
+{
+  MozClirStatus result;
+  result.mN.Construct(aN);
+  result.mM.Construct(aM);
+
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+  if (!ToJSValue(cx, result, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+};
+
+NS_IMETHODIMP
+MobileConnectionCallback::NotifyError(const nsAString& aName,
+                                      const nsAString& aMessage,
+                                      const nsAString& aServiceCode,
+                                      uint16_t aInfo,
+                                      uint8_t aArgc)
+{
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  nsRefPtr<DOMError> error;
+  switch (aArgc) {
+    case 0:
+      return rs->FireErrorAsync(mRequest, aName);
+    case 1:
+      error = new DOMMMIError(mWindow, aName, aMessage, EmptyString(),
+                              Nullable<int16_t>());
+      return rs->FireDetailedError(mRequest, error);
+    case 2:
+      error = new DOMMMIError(mWindow, aName, aMessage, aServiceCode,
+                              Nullable<int16_t>());
+      return rs->FireDetailedError(mRequest, error);
+    case 3:
+      error = new DOMMMIError(mWindow, aName, aMessage, aServiceCode,
+                              Nullable<int16_t>(int16_t(aInfo)));
+      return rs->FireDetailedError(mRequest, error);
+  }
+
+  return NS_ERROR_FAILURE;
+}
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/MobileConnectionCallback.h
@@ -0,0 +1,78 @@
+/* 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_MobileConnectionCallback_h
+#define mozilla_dom_MobileConnectionCallback_h
+
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/MobileConnectionIPCSerializer.h"
+#include "nsCOMPtr.h"
+#include "nsIMobileConnectionService.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * A callback object for handling asynchronous request/response. This object is
+ * created when an asynchronous request is made and should be destroyed after
+ * Notify*Success/Error is called.
+ * The modules hold the reference of MobileConnectionCallback in OOP mode and
+ * non-OOP mode are different.
+ * - OOP mode: MobileConnectionRequestChild
+ * - non-OOP mode: MobileConnectionGonkService
+ * The reference should be released after Notify*Success/Error is called.
+ */
+class MobileConnectionCallback MOZ_FINAL : public nsIMobileConnectionCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMOBILECONNECTIONCALLBACK
+
+  MobileConnectionCallback(nsPIDOMWindow* aWindow, DOMRequest* aRequest);
+
+  /**
+   * Notify Success for Send/CancelMmi.
+   */
+  nsresult
+  NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                             const nsAString& aStatusMessage);
+  nsresult
+  NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                             const nsAString& aStatusMessage,
+                             JS::Handle<JS::Value> aAdditionalInformation);
+  nsresult
+  NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                             const nsAString& aStatusMessage,
+                             uint16_t aAdditionalInformation);
+  nsresult
+  NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                             const nsAString& aStatusMessage,
+                             const nsTArray<nsString>& aAdditionalInformation);
+  nsresult
+  NotifySendCancelMmiSuccess(const nsAString& aServiceCode,
+                             const nsAString& aStatusMessage,
+                             const nsTArray<IPC::MozCallForwardingOptions>& aAdditionalInformation);
+  nsresult
+  NotifySendCancelMmiSuccess(const MozMMIResult& aResult);
+
+  /**
+   * Notify Success for GetCallForwarding.
+   */
+  nsresult
+  NotifyGetCallForwardingSuccess(const nsTArray<IPC::MozCallForwardingOptions>& aResults);
+
+private:
+  ~MobileConnectionCallback() {}
+
+  nsresult
+  NotifySuccess(JS::Handle<JS::Value> aResult);
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
+} // name space dom
+} // name space mozilla
+
+#endif // mozilla_dom_MobileConnectionCallback_h
--- a/dom/mobileconnection/MobileConnectionInfo.cpp
+++ b/dom/mobileconnection/MobileConnectionInfo.cpp
@@ -19,38 +19,87 @@
        entry->value;                                                    \
        ++entry, ++i) {                                                  \
     if (_string.EqualsASCII(entry->value)) {                            \
       _enum.SetValue(static_cast<_enumType>(i));                        \
     }                                                                   \
   }                                                                     \
 }
 
+#define CONVERT_NULLABLE_ENUM_TO_STRING(_enumType, _enum, _string)      \
+{                                                                       \
+  if (_enum.IsNull()) {                                                 \
+    _string.SetIsVoid(true);                                            \
+  } else {                                                              \
+    uint32_t index = uint32_t(_enum.Value());                           \
+    _string.AssignASCII(_enumType##Values::strings[index].value,        \
+                        _enumType##Values::strings[index].length);      \
+  }                                                                     \
+}
+
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MobileConnectionInfo, mWindow,
                                       mNetworkInfo, mCellInfo)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MobileConnectionInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MobileConnectionInfo)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MobileConnectionInfo)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIMobileConnectionInfo)
 NS_INTERFACE_MAP_END
 
 MobileConnectionInfo::MobileConnectionInfo(nsPIDOMWindow* aWindow)
   : mConnected(false)
   , mEmergencyCallsOnly(false)
   , mRoaming(false)
   , mWindow(aWindow)
 {
   SetIsDOMBinding();
 }
 
+MobileConnectionInfo::MobileConnectionInfo(const nsAString& aState,
+                                           bool aConnected,
+                                           bool aEmergencyCallsOnly,
+                                           bool aRoaming,
+                                           nsIMobileNetworkInfo* aNetworkInfo,
+                                           const nsAString& aType,
+                                           const Nullable<int32_t>& aSignalStrength,
+                                           const Nullable<uint16_t>& aRelSignalStrength,
+                                           nsIMobileCellInfo* aCellInfo)
+  : mConnected(aConnected)
+  , mEmergencyCallsOnly(aEmergencyCallsOnly)
+  , mRoaming(aRoaming)
+  , mSignalStrength(aSignalStrength)
+  , mRelSignalStrength(aRelSignalStrength)
+{
+  // The instance created by this way is only used for IPC stuff. It won't be
+  // expose to JS directly, we will clone this instance to the one that is
+  // maintained in MobileConnectionChild. So we don't need SetIsDOMBinding()
+  // here.
+
+  // Update mState and mType
+  CONVERT_STRING_TO_NULLABLE_ENUM(aState, MobileConnectionState, mState);
+  CONVERT_STRING_TO_NULLABLE_ENUM(aType, MobileConnectionType, mType);
+
+  // Update mNetworkInfo
+  if (aNetworkInfo) {
+    mNetworkInfo = new MobileNetworkInfo(mWindow);
+    mNetworkInfo->Update(aNetworkInfo);
+  }
+
+  // Update mCellInfo
+  if (aCellInfo) {
+    mCellInfo = new MobileCellInfo(mWindow);
+    mCellInfo->Update(aCellInfo);
+  }
+}
+
 void
 MobileConnectionInfo::Update(nsIMobileConnectionInfo* aInfo)
 {
   if (!aInfo) {
     return;
   }
 
   aInfo->GetConnected(&mConnected);
@@ -109,10 +158,84 @@ MobileConnectionInfo::Update(nsIMobileCo
   } else {
     mCellInfo = nullptr;
   }
 }
 
 JSObject*
 MobileConnectionInfo::WrapObject(JSContext* aCx)
 {
+  MOZ_ASSERT(IsDOMBinding());
   return MozMobileConnectionInfoBinding::Wrap(aCx, this);
 }
+
+// nsIMobileConnectionInfo
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetState(nsAString& aState)
+{
+  CONVERT_NULLABLE_ENUM_TO_STRING(MobileConnectionState, mState, aState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetConnected(bool* aConnected)
+{
+  *aConnected = Connected();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetEmergencyCallsOnly(bool* aEmergencyCallsOnly)
+{
+  *aEmergencyCallsOnly = EmergencyCallsOnly();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetRoaming(bool* aRoaming)
+{
+  *aRoaming = Roaming();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetNetwork(nsIMobileNetworkInfo** aInfo)
+{
+  NS_IF_ADDREF(*aInfo = GetNetwork());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetType(nsAString& aType)
+{
+  CONVERT_NULLABLE_ENUM_TO_STRING(MobileConnectionType, mType, aType);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetSignalStrength(JS::MutableHandle<JS::Value> aSignal)
+{
+  if (mSignalStrength.IsNull()) {
+    aSignal.setNull();
+  } else {
+    aSignal.setInt32(mSignalStrength.Value());
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetRelSignalStrength(JS::MutableHandle<JS::Value> aSignal)
+{
+  if (mRelSignalStrength.IsNull()) {
+    aSignal.setNull();
+  } else {
+    aSignal.setNumber(uint32_t(mRelSignalStrength.Value()));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionInfo::GetCell(nsIMobileCellInfo** aInfo)
+{
+  NS_IF_ADDREF(*aInfo = GetCell());
+  return NS_OK;
+}
--- a/dom/mobileconnection/MobileConnectionInfo.h
+++ b/dom/mobileconnection/MobileConnectionInfo.h
@@ -12,25 +12,34 @@
 #include "mozilla/dom/MozMobileConnectionInfoBinding.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
-class MobileConnectionInfo MOZ_FINAL : public nsISupports
+class MobileConnectionInfo MOZ_FINAL : public nsIMobileConnectionInfo
                                      , public nsWrapperCache
 {
 public:
+  NS_DECL_NSIMOBILECONNECTIONINFO
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MobileConnectionInfo)
 
   MobileConnectionInfo(nsPIDOMWindow* aWindow);
 
+  MobileConnectionInfo(const nsAString& aState, bool aConnected,
+                       bool aEmergencyCallsOnly, bool aRoaming,
+                       nsIMobileNetworkInfo* aNetworkInfo,
+                       const nsAString& aType,
+                       const Nullable<int32_t>& aSignalStrength,
+                       const Nullable<uint16_t>& aRelSignalStrength,
+                       nsIMobileCellInfo* aCellInfo);
+
   void
   Update(nsIMobileConnectionInfo* aInfo);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
--- a/dom/mobileconnection/MobileNetworkInfo.cpp
+++ b/dom/mobileconnection/MobileNetworkInfo.cpp
@@ -20,16 +20,32 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 MobileNetworkInfo::MobileNetworkInfo(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
 {
   SetIsDOMBinding();
 }
 
+MobileNetworkInfo::MobileNetworkInfo(const nsAString& aShortName,
+                                     const nsAString& aLongName,
+                                     const nsAString& aMcc,
+                                     const nsAString& aMnc,
+                                     const nsAString& aState)
+  : mShortName(aShortName)
+  , mLongName(aLongName)
+  , mMcc(aMcc)
+  , mMnc(aMnc)
+  , mState(aState)
+{
+  // The parent object is nullptr when MobileNetworkInfo is created by this way.
+  // And it won't be exposed to web content.
+  SetIsDOMBinding();
+}
+
 void
 MobileNetworkInfo::Update(nsIMobileNetworkInfo* aInfo)
 {
   if (!aInfo) {
     return;
   }
 
   aInfo->GetShortName(mShortName);
@@ -40,39 +56,16 @@ MobileNetworkInfo::Update(nsIMobileNetwo
 }
 
 JSObject*
 MobileNetworkInfo::WrapObject(JSContext* aCx)
 {
   return MozMobileNetworkInfoBinding::Wrap(aCx, this);
 }
 
-// WebIDL interface
-
-/* static */ already_AddRefed<MobileNetworkInfo>
-MobileNetworkInfo::Constructor(const GlobalObject& aGlobal,
-                               const nsAString& aShortName,
-                               const nsAString& aLongName,
-                               const nsAString& aMcc,
-                               const nsAString& aMnc,
-                               const nsAString& aState,
-                               ErrorResult& aRv)
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  nsRefPtr<MobileNetworkInfo> info = new MobileNetworkInfo(window);
-
-  info->mShortName.Assign(aShortName);
-  info->mLongName.Assign(aLongName);
-  info->mMcc.Assign(aMcc);
-  info->mMnc.Assign(aMnc);
-  info->mState.Assign(aState);
-
-  return info.forget();
-}
-
 // nsIMobileNetworkInfo
 
 NS_IMETHODIMP
 MobileNetworkInfo::GetShortName(nsAString& aShortName)
 {
   aShortName = mShortName;
   return NS_OK;
 }
--- a/dom/mobileconnection/MobileNetworkInfo.h
+++ b/dom/mobileconnection/MobileNetworkInfo.h
@@ -22,35 +22,33 @@ class MobileNetworkInfo MOZ_FINAL : publ
 {
 public:
   NS_DECL_NSIMOBILENETWORKINFO
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MobileNetworkInfo)
 
   MobileNetworkInfo(nsPIDOMWindow* aWindow);
 
+  MobileNetworkInfo(const nsAString& aShortName, const nsAString& aLongName,
+                    const nsAString& aMcc, const nsAString& aMnc,
+                    const nsAString& aState);
+
   void
   Update(nsIMobileNetworkInfo* aInfo);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL interface
-  static already_AddRefed<MobileNetworkInfo>
-  Constructor(const GlobalObject& aGlobal, const nsAString& aShortName,
-              const nsAString& aLongName, const nsAString& aMcc,
-              const nsAString& aMnc, const nsAString& aState,
-              ErrorResult& aRv);
-
   Nullable<MobileNetworkState>
   GetState() const
   {
     uint32_t i = 0;
     for (const EnumEntry* entry = MobileNetworkStateValues::strings;
          entry->value;
          ++entry, ++i) {
       if (mState.EqualsASCII(entry->value)) {
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/gonk/MobileConnectionGonkService.js
@@ -0,0 +1,1548 @@
+/* -*- 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/Services.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var RIL = {};
+Cu.import("resource://gre/modules/ril_consts.js", RIL);
+
+const MOBILECONNECTIONGONKSERVICE_CONTRACTID =
+  "@mozilla.org/mobileconnection/mobileconnectiongonkservice;1";
+
+const MOBILECONNECTIONGONKSERVICE_CID =
+  Components.ID("{05e20430-fe65-4984-8df9-a6a504b24a91}");
+const MOBILENETWORKINFO_CID =
+  Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
+const MOBILECELLINFO_CID =
+  Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}");
+
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID  = "nsPref:changed";
+const NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID = "network-active-changed";
+
+const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
+                                   "@mozilla.org/network/manager;1",
+                                   "nsINetworkManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
+                                   "@mozilla.org/ril;1",
+                                   "nsIRadioInterfaceLayer");
+
+let DEBUG = RIL.DEBUG_RIL;
+function debug(s) {
+  dump("MobileConnectionGonkService: " + s + "\n");
+}
+
+function MobileNetworkInfo() {
+  this.shortName = null;
+  this.longName = null;
+  this.mcc = null;
+  this.mnc = null;
+  this.stat = null;
+}
+MobileNetworkInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileNetworkInfo]),
+  classID:        MOBILENETWORKINFO_CID,
+  classInfo:      XPCOMUtils.generateCI({
+    classID:          MOBILENETWORKINFO_CID,
+    classDescription: "MobileNetworkInfo",
+    interfaces:       [Ci.nsIMobileNetworkInfo]
+  })
+};
+
+function MobileCellInfo() {
+  this.gsmLocationAreaCode = -1;
+  this.gsmCellId = -1;
+  this.cdmaBaseStationId = -1;
+  this.cdmaBaseStationLatitude = -2147483648;
+  this.cdmaBaseStationLongitude = -2147483648;
+  this.cdmaSystemId = -1;
+  this.cdmaNetworkId = -1;
+}
+MobileCellInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileCellInfo]),
+  classID:        MOBILECELLINFO_CID,
+  classInfo:      XPCOMUtils.generateCI({
+    classID:          MOBILECELLINFO_CID,
+    classDescription: "MobileCellInfo",
+    interfaces:       [Ci.nsIMobileCellInfo]
+  })
+};
+
+function CallForwardingOptions(aOptions) {
+  this.active = aOptions.active;
+  this.action = aOptions.action;
+  this.reason = aOptions.reason;
+  this.number = aOptions.number;
+  this.timeSeconds = aOptions.timeSeconds;
+  this.serviceClass = aOptions.serviceClass;
+}
+CallForwardingOptions.prototype = {
+  __exposedProps__ : {active: 'r',
+                      action: 'r',
+                      reason: 'r',
+                      number: 'r',
+                      timeSeconds: 'r',
+                      serviceClass: 'r'},
+};
+
+function MMIResult(aOptions) {
+  this.serviceCode = aOptions.serviceCode;
+  this.statusMessage = aOptions.statusMessage;
+  this.additionalInformation = aOptions.additionalInformation;
+}
+MMIResult.prototype = {
+  __exposedProps__ : {serviceCode: 'r',
+                      statusMessage: 'r',
+                      additionalInformation: 'r'},
+};
+
+function MobileConnectionProvider(aClientId, aRadioInterface) {
+  this._clientId = aClientId;
+  this._radioInterface = aRadioInterface;
+  this._operatorInfo = {};
+  // An array of nsIMobileConnectionListener instances.
+  this._listeners = [];
+
+  this.supportedNetworkTypes = this._getSupportedNetworkTypes();
+  // These objects implement the nsIMobileConnectionInfo interface,
+  // although the actual implementation lives in the content process. So are
+  // the child attributes `network` and `cell`, which implement
+  // nsIMobileNetworkInfo and nsIMobileCellInfo respectively.
+  this.voiceInfo = {connected: false,
+                    emergencyCallsOnly: false,
+                    roaming: false,
+                    network: null,
+                    cell: null,
+                    type: null,
+                    signalStrength: null,
+                    relSignalStrength: null};
+  this.dataInfo = {connected: false,
+                   emergencyCallsOnly: false,
+                   roaming: false,
+                   network: null,
+                   cell: null,
+                   type: null,
+                   signalStrength: null,
+                   relSignalStrength: null};
+}
+MobileConnectionProvider.prototype = {
+  _clientId: null,
+  _radioInterface: null,
+  _operatorInfo: null,
+  _listeners: null,
+
+  /**
+   * The networks that are currently trying to be selected (or "automatic").
+   * This helps ensure that only one network per client is selected at a time.
+   */
+  _selectingNetwork: null,
+
+  voiceInfo: null,
+  dataInfo: null,
+  iccId: null,
+  networkSelectMode: null,
+  radioState: null,
+  lastKnownNetwork: null,
+  lastKnownHomeNetwork: null,
+  supportedNetworkTypes: null,
+
+  /**
+   * A utility function to dump debug message.
+   */
+  _debug: function(aMessage) {
+    dump("MobileConnectionProvider[" + this._clientId + "]: " + aMessage + "\n");
+  },
+
+  /**
+   * A utility function to get supportedNetworkTypes from system property.
+   */
+  _getSupportedNetworkTypes: function() {
+    let key = "ro.moz.ril." + this._clientId + ".network_types";
+    let supportedNetworkTypes = libcutils.property_get(key, "").split(",");
+    for (let type of supportedNetworkTypes) {
+      // If the value in system property is not valid, use the default one which
+      // is defined in ril_consts.js.
+      if (RIL.GECKO_SUPPORTED_NETWORK_TYPES.indexOf(type) < 0) {
+        if (DEBUG) {
+          this._debug("Unknown network type: " + type);
+        }
+        supportedNetworkTypes =
+          RIL.GECKO_SUPPORTED_NETWORK_TYPES_DEFAULT.split(",");
+        break;
+      }
+    }
+    if (DEBUG) {
+      this._debug("Supported Network Types: " + supportedNetworkTypes);
+    }
+    return supportedNetworkTypes;
+  },
+
+  /**
+   * Helper for guarding us against invalid reason values for call forwarding.
+   */
+  _isValidCallForwardingReason: function(aReason) {
+    switch (aReason) {
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_UNCONDITIONAL:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_MOBILE_BUSY:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_NO_REPLY:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_NOT_REACHABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_ALL_CALL_FORWARDING:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING:
+        return true;
+      default:
+        return false;
+    }
+  },
+
+  /**
+   * Helper for guarding us against invalid action values for call forwarding.
+   */
+  _isValidCallForwardingAction: function(aAction) {
+    switch (aAction) {
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_DISABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_ENABLE:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_REGISTRATION:
+      case Ci.nsIMobileConnectionService.CALL_FORWARD_ACTION_ERASURE:
+        return true;
+      default:
+        return false;
+    }
+  },
+
+  /**
+   * Helper for guarding us against invalid program values for call barring.
+   */
+  _isValidCallBarringProgram: function(aProgram) {
+    switch (aProgram) {
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_ALL_OUTGOING:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_ALL_INCOMING:
+      case Ci.nsIMobileConnectionService.CALL_BARRING_PROGRAM_INCOMING_ROAMING:
+        return true;
+      default:
+        return false;
+    }
+  },
+
+  /**
+   * Helper for guarding us against invalid options for call barring.
+   */
+  _isValidCallBarringOptions: function(aOptions, aUsedForSetting) {
+    if (!aOptions || aOptions.serviceClass == null ||
+        !this._isValidCallBarringProgram(aOptions.program)) {
+      return false;
+    }
+
+    // For setting callbarring options, |enabled| and |password| are required.
+    if (aUsedForSetting &&
+        (aOptions.enabled == null || aOptions.password == null)) {
+      return false;
+    }
+
+    return true;
+  },
+
+  /**
+   * Helper for guarding us against invalid mode for clir.
+   */
+  _isValidClirMode: function(aMode) {
+    switch (aMode) {
+      case Ci.nsIMobileConnectionService.CLIR_DEFAULT:
+      case Ci.nsIMobileConnectionService.CLIR_INVOCATION:
+      case Ci.nsIMobileConnectionService.CLIR_SUPPRESSION:
+        return true;
+      default:
+        return false;
+    }
+  },
+
+  /**
+   * Fix the roaming. RIL can report roaming in some case it is not
+   * really the case. See bug 787967
+   */
+  _checkRoamingBetweenOperators: function(aNetworkInfo) {
+    // TODO: Bug 864489 - B2G RIL: use ipdl as IPC in MozIccManager
+    // Should get iccInfo from IccGonkProvider.
+    let iccInfo = this._radioInterface.rilContext.iccInfo;
+    let operator = aNetworkInfo.network;
+    let state = aNetworkInfo.state;
+
+    if (!iccInfo || !operator ||
+        state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      return false;
+    }
+
+    let spn = iccInfo.spn && iccInfo.spn.toLowerCase();
+    let longName = operator.longName && operator.longName.toLowerCase();
+    let shortName = operator.shortName && operator.shortName.toLowerCase();
+
+    let equalsLongName = longName && (spn == longName);
+    let equalsShortName = shortName && (spn == shortName);
+    let equalsMcc = iccInfo.mcc == operator.mcc;
+
+    let newRoaming = aNetworkInfo.roaming &&
+                     !(equalsMcc && (equalsLongName || equalsShortName));
+    if (newRoaming === aNetworkInfo.roaming) {
+      return false;
+    }
+
+    aNetworkInfo.roaming = newRoaming;
+    return true;
+  },
+
+  _updateInfo: function(aDestInfo, aSrcInfo) {
+    let isUpdated = false;
+    for (let key in aSrcInfo) {
+      // For updating MobileConnectionInfo
+      if (key === "cell" && aSrcInfo.cell) {
+        if (!aDestInfo.cell) {
+          aDestInfo.cell = new MobileCellInfo();
+        }
+        isUpdated = this._updateInfo(aDestInfo.cell, aSrcInfo.cell) || isUpdated;
+      } else if (aDestInfo[key] !== aSrcInfo[key]) {
+        isUpdated = true;
+        aDestInfo[key] = aSrcInfo[key];
+      }
+    }
+    return isUpdated;
+  },
+
+  _rulesToCallForwardingOptions: function(aRules) {
+    for (let i = 0; i < aRules.length; i++) {
+      let info = new CallForwardingOptions(aRules[i]);
+      aRules[i] = info;
+    }
+  },
+
+  _dispatchNotifyError: function(aCallback, aErrorMsg) {
+    Services.tm.currentThread.dispatch(() => aCallback.notifyError(aErrorMsg),
+                                       Ci.nsIThread.DISPATCH_NORMAL);
+  },
+
+  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) {
+      this._listeners.splice(index, 1);
+    }
+  },
+
+  deliverListenerEvent: function(aName, aArgs) {
+    let listeners = this._listeners.slice();
+    for (let listener of listeners) {
+      if (listeners.indexOf(listener) === -1) {
+        continue;
+      }
+      let handler = listener[aName];
+      if (typeof handler != "function") {
+        throw new Error("No handler for " + aName);
+      }
+      try {
+        handler.apply(listener, aArgs);
+      } catch (e) {
+        if (DEBUG) {
+          this._debug("listener for " + aName + " threw an exception: " + e);
+        }
+      }
+    }
+  },
+
+  updateVoiceInfo: function(aNewInfo, aBatch = false) {
+    let isUpdated = this._updateInfo(this.voiceInfo, aNewInfo);
+
+    // Make sure we also reset the operator and signal strength information
+    // if we drop off the network.
+    if (this.voiceInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      this.voiceInfo.cell = null;
+      this.voiceInfo.network = null;
+      this.voiceInfo.signalStrength = null;
+      this.voiceInfo.relSignalStrength = null;
+    } else {
+      this.voiceInfo.network = this._operatorInfo;
+    }
+
+    // Check roaming state
+    isUpdated = this._checkRoamingBetweenOperators(this.voiceInfo) || isUpdated;
+
+    if (isUpdated && !aBatch) {
+      this.deliverListenerEvent("notifyVoiceChanged");
+    }
+  },
+
+  updateDataInfo: function(aNewInfo, aBatch = false) {
+    let isUpdated = false;
+
+    // For the data connection, the `connected` flag indicates whether
+    // there's an active data call. We get correct `connected` state here.
+    let active = gNetworkManager.active;
+    aNewInfo.connected = false;
+    if (active &&
+        active.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
+        active.serviceId === this._clientId) {
+      aNewInfo.connected = true;
+    }
+
+    isUpdated = this._updateInfo(this.dataInfo, aNewInfo);
+
+    // Make sure we also reset the operator and signal strength information
+    // if we drop off the network.
+    if (this.dataInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      this.dataInfo.cell = null;
+      this.dataInfo.network = null;
+      this.dataInfo.signalStrength = null;
+      this.dataInfo.relSignalStrength = null;
+    } else {
+      this.dataInfo.network = this._operatorInfo;
+    }
+
+    // Check roaming state
+    isUpdated = this._checkRoamingBetweenOperators(this.dataInfo) || isUpdated;
+
+    if (isUpdated && !aBatch) {
+      this.deliverListenerEvent("notifyDataChanged");
+    }
+  },
+
+  updateOperatorInfo: function(aNewInfo, aBatch = false) {
+    let isUpdated = this._updateInfo(this._operatorInfo, aNewInfo);
+
+    // Update lastKnownNetwork
+    if (this._operatorInfo.mcc && this._operatorInfo.mnc) {
+      let network = this._operatorInfo.mcc + "-" + this._operatorInfo.mnc;
+      if (this.lastKnownNetwork !== network) {
+        if (DEBUG) {
+          this._debug("lastKnownNetwork now is " + network);
+        }
+
+        this.lastKnownNetwork = network;
+        this.deliverListenerEvent("notifyLastKnownNetworkChanged");
+      }
+    }
+
+    // If the voice is unregistered, no need to send notification.
+    if (this.voiceInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
+        isUpdated && !aBatch) {
+      this.deliverListenerEvent("notifyVoiceChanged");
+    }
+
+    // If the data is unregistered, no need to send notification.
+    if (this.dataInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
+        isUpdated && !aBatch) {
+      this.deliverListenerEvent("notifyDataChanged");
+    }
+  },
+
+  updateSignalInfo: function(aNewInfo, aBatch = false) {
+    // If the voice is not registered, no need to update signal information.
+    if (this.voiceInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      if (this._updateInfo(this.voiceInfo, aNewInfo.voice) && !aBatch) {
+        this.deliverListenerEvent("notifyVoiceChanged");
+      }
+    }
+
+    // If the data is not registered, no need to update signal information.
+    if (this.dataInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
+      if (this._updateInfo(this.dataInfo, aNewInfo.data) && !aBatch) {
+        this.deliverListenerEvent("notifyDataChanged");
+      }
+    }
+  },
+
+  updateIccId: function(aIccId) {
+    if (this.iccId === aIccId) {
+      return;
+    }
+
+    this.iccId = aIccId;
+    this.deliverListenerEvent("notifyIccChanged");
+  },
+
+  updateRadioState: function(aRadioState) {
+    if (this.radioState === aRadioState) {
+      return;
+    }
+
+    this.radioState = aRadioState;
+    this.deliverListenerEvent("notifyRadioStateChanged");
+  },
+
+  getNetworks: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("getAvailableNetworks", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      let networks = aResponse.networks;
+      for (let i = 0; i < networks.length; i++) {
+        let info = new MobileNetworkInfo();
+        this._updateInfo(info, networks[i]);
+        networks[i] = info;
+      }
+
+      aCallback.notifyGetNetworksSuccess(networks.length, networks);
+      return false;
+    }).bind(this));
+  },
+
+  selectNetwork: function(aNetwork, aCallback) {
+    if (!aNetwork ||
+        isNaN(parseInt(aNetwork.mcc, 10)) ||
+        isNaN(parseInt(aNetwork.mnc, 10))) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    if (this._selectingNetwork) {
+      this._dispatchNotifyError(aCallback, "AlreadySelectingANetwork");
+      return;
+    }
+
+    let options = {mcc: aNetwork.mcc, mnc: aNetwork.mnc};
+    this._selectingNetwork = options;
+    this._radioInterface.sendWorkerMessage("selectNetwork", options,
+                                           (function(aResponse) {
+      this._selectingNetwork = null;
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  selectNetworkAutomatically: function(aCallback) {
+    if (this._selectingNetwork) {
+      this._dispatchNotifyError(aCallback, "AlreadySelectingANetwork");
+      return;
+    }
+
+    this._selectingNetwork = "automatic";
+    this._radioInterface.sendWorkerMessage("selectNetworkAuto", null,
+                                           (function(aResponse) {
+      this._selectingNetwork = null;
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  setPreferredNetworkType: function(aType, aCallback) {
+    if (this.radioState !== RIL.GECKO_RADIOSTATE_ENABLED) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE);
+      return;
+    }
+
+    this._radioInterface.sendWorkerMessage("setPreferredNetworkType",
+                                           {type: aType},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getPreferredNetworkType: function(aCallback) {
+    if (this.radioState !== RIL.GECKO_RADIOSTATE_ENABLED) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE);
+      return;
+    }
+
+    this._radioInterface.sendWorkerMessage("getPreferredNetworkType", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccessWithString(aResponse.type);
+      return false;
+    }).bind(this));
+  },
+
+  setRoamingPreference: function(aMode, aCallback) {
+    this._radioInterface.sendWorkerMessage("setRoamingPreference",
+                                           {mode: aMode},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getRoamingPreference: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("queryRoamingPreference", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccessWithString(aResponse.mode);
+      return false;
+    }).bind(this));
+  },
+
+  setVoicePrivacyMode: function(aEnabled, aCallback) {
+    this._radioInterface.sendWorkerMessage("setVoicePrivacyMode",
+                                           {enabled: aEnabled},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getVoicePrivacyMode: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("queryVoicePrivacyMode", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccessWithBoolean(aResponse.enabled);
+      return false;
+    }).bind(this));
+  },
+
+  sendMMI: function(aMmi, aCallback) {
+    this._radioInterface.sendWorkerMessage("sendMMI", {mmi: aMmi},
+                                           (function(aResponse) {
+      aResponse.serviceCode = aResponse.mmiServiceCode || "";
+      // We expect to have an IMEI at this point if the request was supposed
+      // to query for the IMEI, so getting a successful reply from the RIL
+      // without containing an actual IMEI number is considered an error.
+      if (aResponse.serviceCode === RIL.MMI_KS_SC_IMEI &&
+          !aResponse.statusMessage) {
+        aResponse.errorMsg = aResponse.errorMsg ||
+                             RIL.GECKO_ERROR_GENERIC_FAILURE;
+      }
+
+      if (aResponse.errorMsg) {
+        if (aResponse.additionalInformation) {
+          aCallback.notifyError(aResponse.errorMsg, "",
+                                aResponse.serviceCode,
+                                aResponse.additionalInformation);
+        } else {
+          aCallback.notifyError(aResponse.errorMsg, "",
+                                aResponse.serviceCode);
+        }
+        return false;
+      }
+
+      if (aResponse.isSetCallForward) {
+        this.deliverListenerEvent("notifyCFStateChanged",
+                                  [!aResponse.errorMsg, aResponse.action,
+                                   aResponse.reason, aResponse.number,
+                                   aResponse.timeSeconds, aResponse.serviceClass]);
+      }
+
+      // MMI query call forwarding options request returns a set of rules that
+      // will be exposed in the form of an array of MozCallForwardingOptions
+      // instances.
+      if (aResponse.serviceCode === RIL.MMI_KS_SC_CALL_FORWARDING &&
+          aResponse.additionalInformation) {
+        this._rulesToCallForwardingOptions(aResponse.additionalInformation);
+      }
+
+      let mmiResult = new MMIResult(aResponse);
+      aCallback.notifySendCancelMmiSuccess(mmiResult);
+      return false;
+    }).bind(this));
+  },
+
+  cancelMMI: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("cancelUSSD", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  setCallForwarding: function(aOptions, aCallback) {
+    if (!aOptions ||
+        !this._isValidCallForwardingReason(aOptions.reason) ||
+        !this._isValidCallForwardingAction(aOptions.action)){
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    let options = {
+      active: aOptions.active,
+      action: aOptions.action,
+      reason: aOptions.reason,
+      number: aOptions.number,
+      timeSeconds: aOptions.timeSeconds,
+      serviceClass: RIL.ICC_SERVICE_CLASS_VOICE
+    };
+
+    this._radioInterface.sendWorkerMessage("setCallForward", options,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      this.deliverListenerEvent("notifyCFStateChanged",
+                                [!aResponse.errorMsg, aResponse.action,
+                                 aResponse.reason, aResponse.number,
+                                 aResponse.timeSeconds, aResponse.serviceClass]);
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getCallForwarding: function(aReason, aCallback) {
+    if (!this._isValidCallForwardingReason(aReason)){
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    this._radioInterface.sendWorkerMessage("queryCallForwardStatus",
+                                           {reason: aReason},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      let infos = aResponse.rules;
+      this._rulesToCallForwardingOptions(infos);
+      aCallback.notifyGetCallForwardingSuccess(infos);
+      return false;
+    }).bind(this));
+  },
+
+  setCallBarring: function(aOptions, aCallback) {
+    if (!this._isValidCallBarringOptions(aOptions, true)) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    let options = {
+      program: aOptions.program,
+      enabled: aOptions.enabled,
+      password: aOptions.password,
+      serviceClass: aOptions.serviceClass
+    };
+
+    this._radioInterface.sendWorkerMessage("setCallBarring", options,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getCallBarring: function(aOptions, aCallback) {
+    if (!this._isValidCallBarringOptions(aOptions)) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    let options = {
+      program: aOptions.program,
+      password: aOptions.password,
+      serviceClass: aOptions.serviceClass
+    };
+
+    this._radioInterface.sendWorkerMessage("queryCallBarringStatus", options,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifyGetCallBarringSuccess(aResponse.program,
+                                            aResponse.enabled,
+                                            aResponse.serviceClass);
+      return false;
+    }).bind(this));
+  },
+
+  changeCallBarringPassword: function(aOptions, aCallback) {
+    // Checking valid PIN for supplementary services. See TS.22.004 clause 5.2.
+    if (aOptions.pin == null || !aOptions.pin.match(/^\d{4}$/) ||
+        aOptions.newPin == null || !aOptions.newPin.match(/^\d{4}$/)) {
+      this._dispatchNotifyError(aCallback, "InvalidPassword");
+      return;
+    }
+
+    let options = {
+      pin: aOptions.pin,
+      newPin: aOptions.newPin
+    };
+
+    this._radioInterface.sendWorkerMessage("changeCallBarringPassword", options,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  setCallWaiting: function(aEnabled, aCallback) {
+    this._radioInterface.sendWorkerMessage("setCallWaiting",
+                                           {enabled: aEnabled},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getCallWaiting: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("queryCallWaiting", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccessWithBoolean(aResponse.enabled);
+      return false;
+    }).bind(this));
+  },
+
+  setCallingLineIdRestriction: function(aMode, aCallback) {
+    if (!this._isValidClirMode(aMode)) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    if (this.radioState !== RIL.GECKO_RADIOSTATE_ENABLED) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE);
+      return;
+    }
+
+    this._radioInterface.sendWorkerMessage("setCLIR", {clirMode: aMode},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      this.deliverListenerEvent("notifyClirModeChanged", [aResponse.mode]);
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  getCallingLineIdRestriction: function(aCallback) {
+    if (this.radioState !== RIL.GECKO_RADIOSTATE_ENABLED) {
+      this._dispatchNotifyError(aCallback, RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE);
+      return;
+    }
+
+    this._radioInterface.sendWorkerMessage("getCLIR", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifyGetClirStatusSuccess(aResponse.n, aResponse.m);
+      return false;
+    }).bind(this));
+  },
+
+  exitEmergencyCbMode: function(aCallback) {
+    this._radioInterface.sendWorkerMessage("exitEmergencyCbMode", null,
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return false;
+      }
+
+      aCallback.notifySuccess();
+      return false;
+    }).bind(this));
+  },
+
+  setRadioEnabled: function(aEnabled, aCallback) {
+    this._radioInterface.sendWorkerMessage("setRadioEnabled",
+                                           {enabled: aEnabled},
+                                           (function(aResponse) {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return true;
+      }
+
+      aCallback.notifySuccess();
+      return true;
+    }).bind(this));
+  },
+};
+
+function MobileConnectionGonkService() {
+  this._providers = [];
+
+  let numClients = gRadioInterfaceLayer.numRadioInterfaces;
+  for (let i = 0; i < numClients; i++) {
+    let radioInterface = gRadioInterfaceLayer.getRadioInterface(i);
+    let provider = new MobileConnectionProvider(i, radioInterface);
+    this._providers.push(provider);
+  }
+
+  Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
+  Services.obs.addObserver(this, NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID, false);
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+
+  debug("init complete");
+}
+MobileConnectionGonkService.prototype = {
+  classID: MOBILECONNECTIONGONKSERVICE_CID,
+  classInfo: XPCOMUtils.generateCI({classID: MOBILECONNECTIONGONKSERVICE_CID,
+                                    contractID: MOBILECONNECTIONGONKSERVICE_CONTRACTID,
+                                    classDescription: "MobileConnectionGonkService",
+                                    interfaces: [Ci.nsIMobileConnectionGonkService,
+                                                 Ci.nsIMobileConnectionService],
+                                    flags: Ci.nsIClassInfo.SINGLETON}),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionGonkService,
+                                         Ci.nsIMobileConnectionService,
+                                         Ci.nsIObserver]),
+
+  // An array of MobileConnectionProvider instances.
+  _providers: null,
+
+  _shutdown: function() {
+    Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
+    Services.obs.removeObserver(this, NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID);
+    Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+  },
+
+  _updateDebugFlag: function() {
+    try {
+      DEBUG = RIL.DEBUG_RIL ||
+              Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
+    } catch (e) {}
+  },
+
+  /**
+   * nsIMobileConnectionService interface.
+   */
+  registerListener: function(aClientId, aListener) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.registerListener(aListener);
+  },
+
+  unregisterListener: function(aClientId, aListener) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.unregisterListener(aListener);
+  },
+
+  getVoiceConnectionInfo: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.voiceInfo;
+  },
+
+  getDataConnectionInfo: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.dataInfo;
+  },
+
+  getIccId: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.iccId;
+  },
+
+  getNetworkSelectionMode: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.networkSelectMode;
+  },
+
+  getRadioState: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.radioState;
+  },
+
+  getLastKnownNetwork: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.lastKnownNetwork;
+  },
+
+  getLastKnownHomeNetwork: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.lastKnownHomeNetwork;
+  },
+
+  getSupportedNetworkTypes: function(aClientId) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return provider.supportedNetworkTypes;
+  },
+
+  getNetworks: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getNetworks(aCallback);
+  },
+
+  selectNetwork: function(aClientId, aNetwork, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.selectNetwork(aNetwork, aCallback);
+  },
+
+  selectNetworkAutomatically: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.selectNetworkAutomatically(aCallback);
+  },
+
+  setPreferredNetworkType: function(aClientId, aType, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setPreferredNetworkType(aType, aCallback);
+  },
+
+  getPreferredNetworkType: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getPreferredNetworkType(aCallback);
+  },
+
+  setRoamingPreference: function(aClientId, aMode, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setRoamingPreference(aMode, aCallback);
+  },
+
+  getRoamingPreference: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getRoamingPreference(aCallback);
+  },
+
+  setVoicePrivacyMode: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setVoicePrivacyMode(aEnabled, aCallback);
+  },
+
+  getVoicePrivacyMode: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getVoicePrivacyMode(aCallback);
+  },
+
+  sendMMI: function(aClientId, aMmi, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.sendMMI(aMmi, aCallback);
+  },
+
+  cancelMMI: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.cancelMMI(aCallback);
+  },
+
+  setCallForwarding: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallForwarding(aOptions, aCallback);
+  },
+
+  getCallForwarding: function(aClientId, aReason, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallForwarding(aReason, aCallback);
+  },
+
+  setCallBarring: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallBarring(aOptions, aCallback);
+  },
+
+  getCallBarring: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallBarring(aOptions, aCallback);
+  },
+
+  changeCallBarringPassword: function(aClientId, aOptions, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.changeCallBarringPassword(aOptions, aCallback);
+  },
+
+  setCallWaiting: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallWaiting(aEnabled, aCallback);
+  },
+
+  getCallWaiting: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallWaiting(aCallback);
+  },
+
+  setCallingLineIdRestriction: function(aClientId, aMode, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setCallingLineIdRestriction(aMode, aCallback);
+  },
+
+  getCallingLineIdRestriction: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.getCallingLineIdRestriction(aCallback);
+  },
+
+  exitEmergencyCbMode: function(aClientId, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.exitEmergencyCbMode(aCallback);
+  },
+
+  setRadioEnabled: function(aClientId, aEnabled, aCallback) {
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.setRadioEnabled(aEnabled, aCallback);
+  },
+
+  /**
+   * nsIMobileConnectionGonkService interface.
+   */
+  notifyVoiceInfoChanged: function(aClientId, aVoiceInfo) {
+    if (DEBUG) {
+      debug("notifyVoiceInfoChanged for " + aClientId + ": " +
+            JSON.stringify(aVoiceInfo));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateVoiceInfo(aVoiceInfo);
+  },
+
+  notifyDataInfoChanged: function(aClientId, aDataInfo) {
+    if (DEBUG) {
+      debug("notifyDataInfoChanged for " + aClientId + ": " +
+            JSON.stringify(aDataInfo));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateDataInfo(aDataInfo);
+  },
+
+  notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
+    if (DEBUG) {
+      debug("notifyUssdReceived for " + aClientId + ": " +
+            JSON.stringify(ussd));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyUssdReceived",
+                                [aMessage, aSessionEnded]);
+
+    let info = {
+      message: aMessage,
+      sessionEnded: aSessionEnded,
+      serviceId: aClientId
+    };
+
+    gSystemMessenger.broadcastMessage("ussd-received", info);
+  },
+
+  notifyDataError: function(aClientId, aMessage) {
+    if (DEBUG) {
+      debug("notifyDataError for " + aClientId + ": " + aMessage);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyDataError", [aMessage]);
+  },
+
+  notifyEmergencyCallbackModeChanged: function(aClientId, aActive, aTimeoutMs) {
+    if (DEBUG) {
+      debug("notifyEmergencyCbModeChanged for " + aClientId + ": " +
+            JSON.stringify({active: aActive, timeoutMs: aTimeoutMs}));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyEmergencyCbModeChanged",
+                                [aActive, aTimeoutMs]);
+  },
+
+  notifyOtaStatusChanged: function(aClientId, aStatus) {
+    if (DEBUG) {
+      debug("notifyOtaStatusChanged for " + aClientId + ": " + aStatus);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.deliverListenerEvent("notifyOtaStatusChanged", [aStatus]);
+  },
+
+  notifyIccChanged: function(aClientId, aIccId) {
+    if (DEBUG) {
+      debug("notifyIccChanged for " + aClientId + ": " + aIccId);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateIccId(aIccId);
+  },
+
+  notifyRadioStateChanged: function(aClientId, aRadioState) {
+    if (DEBUG) {
+      debug("notifyRadioStateChanged for " + aClientId + ": " + aRadioState);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateRadioState(aRadioState);
+  },
+
+  notifyNetworkInfoChanged: function(aClientId, aNetworkInfo) {
+    if (DEBUG) {
+      debug("notifyNetworkInfoChanged for " + aClientId + ": " +
+            JSON.stringify(aNetworkInfo));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    let isVoiceUpdated = false;
+    let isDataUpdated = false;
+    let operatorMessage = aNetworkInfo[RIL.NETWORK_INFO_OPERATOR];
+    let voiceMessage = aNetworkInfo[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
+    let dataMessage = aNetworkInfo[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
+    let signalMessage = aNetworkInfo[RIL.NETWORK_INFO_SIGNAL];
+    let selectionMessage = aNetworkInfo[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
+
+    // Batch the *InfoChanged messages together
+    if (operatorMessage) {
+      provider.updateOperatorInfo(operatorMessage, true);
+    }
+
+    if (voiceMessage) {
+      provider.updateVoiceInfo(voiceMessage, true);
+    }
+
+    if (dataMessage) {
+      provider.updateDataInfo(dataMessage, true);
+    }
+
+    if (signalMessage) {
+      provider.updateSignalInfo(signalMessage, true);
+    }
+
+    if (selectionMessage) {
+      this.notifyNetworkSelectModeChanged(aClientId, selectionMessage.mode);
+    }
+
+    if (voiceMessage || operatorMessage || signalMessage) {
+      provider.deliverListenerEvent("notifyVoiceChanged");
+    }
+
+    if (dataMessage || operatorMessage || signalMessage) {
+      provider.deliverListenerEvent("notifyDataChanged");
+    }
+  },
+
+  notifySignalStrengthChanged: function(aClientId, aSignal) {
+    if (DEBUG) {
+      debug("notifySignalStrengthChanged for " + aClientId + ": " +
+            JSON.stringify(aSignal));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateSignalInfo(aSignal);
+  },
+
+  notifyOperatorChanged: function(aClientId, aOperator) {
+    if (DEBUG) {
+      debug("notifyOperatorChanged for " + aClientId + ": " +
+            JSON.stringify(aOperator));
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    provider.updateOperatorInfo(aOperator);
+  },
+
+  notifyNetworkSelectModeChanged: function(aClientId, aMode) {
+    if (DEBUG) {
+      debug("notifyNetworkSelectModeChanged for " + aClientId + ": " + aMode);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    if (provider.networkSelectMode === aMode) {
+      return;
+    }
+
+    provider.networkSelectMode = aMode;
+    provider.deliverListenerEvent("notifyNetworkSelectionModeChanged");
+  },
+
+  notifySpnAvailable: function(aClientId) {
+    if (DEBUG) {
+      debug("notifySpnAvailable for " + aClientId);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    // Update voice roaming state
+    provider.updateVoiceInfo({});
+
+    // Update data roaming state
+    provider.updateDataInfo({});
+  },
+
+  notifyLastHomeNetworkChanged: function(aClientId, aNetwork) {
+    if (DEBUG) {
+      debug("notifyLastHomeNetworkChanged for " + aClientId + ": " + aNetwork);
+    }
+
+    let provider = this._providers[aClientId];
+    if (!provider) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    if (provider.lastKnownHomeNetwork === aNetwork) {
+      return;
+    }
+
+    provider.lastKnownHomeNetwork = aNetwork;
+    provider.deliverListenerEvent("notifyLastKnownHomeNetworkChanged");
+  },
+
+  /**
+   * nsIObserver interface.
+   */
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID:
+        for (let i = 0; i < this._providers.length; i++) {
+          let provider = this._providers[i];
+          // Update connected flag only.
+          provider.updateDataInfo({});
+        }
+        break;
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (aData === kPrefRilDebuggingEnabled) {
+          this._updateDebugFlag();
+        }
+        break;
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
+        this._shutdown();
+        break;
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MobileConnectionGonkService]);
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/gonk/MobileConnectionGonkService.manifest
@@ -0,0 +1,2 @@
+component {05e20430-fe65-4984-8df9-a6a504b24a91} MobileConnectionGonkService.js
+contract @mozilla.org/mobileconnection/mobileconnectiongonkservice;1 {05e20430-fe65-4984-8df9-a6a504b24a91}
--- a/dom/mobileconnection/interfaces/moz.build
+++ b/dom/mobileconnection/interfaces/moz.build
@@ -3,14 +3,19 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsICellInfo.idl',
     'nsIMobileCellInfo.idl',
     'nsIMobileConnectionInfo.idl',
-    'nsIMobileConnectionProvider.idl',
+    'nsIMobileConnectionService.idl',
     'nsIMobileNetworkInfo.idl',
     'nsINeighboringCellInfo.idl',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
+    XPIDL_SOURCES += [
+        'nsIMobileConnectionGonkService.idl',
+    ]
+
 XPIDL_MODULE = 'dom_mobileconnection'
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/interfaces/nsIMobileConnectionGonkService.idl
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMobileConnectionService.idl"
+
+%{C++
+#define NS_MOBILECONNECTION_GONK_SERVICE_CONTRACTID \
+        "@mozilla.org/mobileconnection/mobileconnectiongonkservice;1"
+%}
+
+[scriptable, uuid(c5baceda-247a-4018-855d-ad5b00f2e4e2)]
+interface nsIMobileConnectionGonkService : nsIMobileConnectionService
+{
+  void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo);
+
+  void notifyVoiceInfoChanged(in unsigned long clientId, in jsval voiceInfo);
+
+  void notifyDataInfoChanged(in unsigned long clientId, in jsval dataInfo);
+
+  void notifyDataError(in unsigned long clientId, in DOMString message);
+
+  void notifySignalStrengthChanged(in unsigned long clientId, in jsval signal);
+
+  void notifyOperatorChanged(in unsigned long clientId, in jsval info);
+
+  void notifyOtaStatusChanged(in unsigned long clientId, in DOMString status);
+
+  void notifyRadioStateChanged(in unsigned long clientId,
+                               in DOMString radioState);
+
+  void notifyUssdReceived(in unsigned long clientId,
+                          in DOMString message,
+                          in boolean sessionEnded);
+
+  void notifyEmergencyCallbackModeChanged(in unsigned long clientId,
+                                          in boolean active,
+                                          in unsigned long timeoutMs);
+
+  void notifyIccChanged(in unsigned long clientId, in DOMString iccId);
+
+  void notifyNetworkSelectModeChanged(in unsigned long clientId,
+                                      in DOMString mode);
+
+  void notifySpnAvailable(in unsigned long clientId);
+
+  void notifyLastHomeNetworkChanged(in unsigned long clientId,
+                                    in DOMString network);
+};
rename from dom/mobileconnection/interfaces/nsIMobileConnectionProvider.idl
rename to dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
--- a/dom/mobileconnection/interfaces/nsIMobileConnectionProvider.idl
+++ b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
@@ -1,21 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsIDOMDOMRequest;
-interface nsIDOMWindow;
 interface nsIMobileConnectionInfo;
 interface nsIMobileNetworkInfo;
 interface nsIVariant;
 
-[scriptable, uuid(c6d98e6a-d96f-45fe-aa86-01453a6daf9e)]
+[scriptable, uuid(bc0d4d76-fd3a-4593-818f-cb6ff87fbb55)]
 interface nsIMobileConnectionListener : nsISupports
 {
   /**
    * Notify when voice info is changed.
    */
   void notifyVoiceChanged();
 
   /**
@@ -43,32 +41,32 @@ interface nsIMobileConnectionListener : 
   void notifyDataError(in DOMString message);
 
   /**
    * Notify when call forwarding state is changed.
    *
    * @param success
    *        Indicates whether the set call forwarding request is success.
    * @param action
-   *        One of the nsIMobileConnectionProvider.CALL_FORWARD_ACTION_* values.
+   *        One of the nsIMobileConnectionService.CALL_FORWARD_ACTION_* values.
    * @param reason
-   *        One of the nsIMobileConnectionProvider.CALL_FORWARD_REASON_* values.
+   *        One of the nsIMobileConnectionService.CALL_FORWARD_REASON_* values.
    * @param number
    *        Phone number of forwarding address.
    * @param timeSeconds
    *        The time in seconds should wait before call is forwarded.
    * @param serviceClass
-   *        One of the nsIMobileConnectionProvider.ICC_SERVICE_CLASS_* values.
+   *        One of the nsIMobileConnectionService.ICC_SERVICE_CLASS_* values.
    */
-  void notifyCFStateChange(in boolean success,
-                           in unsigned short action,
-                           in unsigned short reason,
-                           in DOMString number,
-                           in unsigned short timeSeconds,
-                           in unsigned short serviceClass);
+  void notifyCFStateChanged(in boolean success,
+                            in unsigned short action,
+                            in unsigned short reason,
+                            in DOMString number,
+                            in unsigned short timeSeconds,
+                            in unsigned short serviceClass);
 
   /**
    * Notify when emergency callback mode is changed.
    *
    * @param active
    *        Indicates whether the emergency callback mode is activated.
    * @param timeoutMs
    *        The timeout in millisecond for emergency callback mode.
@@ -96,27 +94,123 @@ interface nsIMobileConnectionListener : 
    * Notify when radio state is changed.
    */
   void notifyRadioStateChanged();
 
   /**
    * Notify when clir mode is changed.
    *
    * @param mode
-   *        One of the nsIMobileConnectionProvider.CLIR_* values.
+   *        One of the nsIMobileConnectionService.CLIR_* values.
    */
   void notifyClirModeChanged(in unsigned long mode);
+
+  /**
+   * Notify when last known network is changed.
+   */
+  void notifyLastKnownNetworkChanged();
+
+  /**
+   * Notify when last known home network is changed.
+   */
+  void notifyLastKnownHomeNetworkChanged();
+
+  /**
+   * Notify when network selection mode is changed.
+   */
+  void notifyNetworkSelectionModeChanged();
 };
 
+%{C++
+#define NO_ADDITIONAL_INFORMATION 0
+%}
+
+[scriptable, builtinclass, uuid(e9d7c247-34c6-42bf-875b-f99b19db394f)]
+interface nsIMobileConnectionCallback : nsISupports
+{
+  /**
+   * notify*Success*() will be called, when request is succeed.
+   */
+  void notifySuccess();
+
+  void notifySuccessWithString(in DOMString result);
+
+  void notifySuccessWithBoolean(in boolean result);
+
+  void notifyGetNetworksSuccess(in uint32_t count,
+                                [array, size_is(count)] in nsIMobileNetworkInfo networks);
+
+  void notifySendCancelMmiSuccess(in jsval result /* MozMMIResult */);
+
+  void notifyGetCallForwardingSuccess(in jsval results /* Array of MozCallForwardingOptions */);
+
+  void notifyGetCallBarringSuccess(in unsigned short program,
+                                   in boolean enabled,
+                                   in unsigned short serviceClass);
+
+  void notifyGetClirStatusSuccess(in unsigned short n, in unsigned short m);
+
+  /**
+   * notifyError() will be called, when request is failed.
+   */
+  [optional_argc]
+  void notifyError(in DOMString name,
+                   [optional] in DOMString message,
+                   [optional] in DOMString serviceCode,
+                   [optional] in unsigned short additionalInformation);
+
+%{C++
+  // non-virtual so it won't affect the vtable
+  inline nsresult NotifyError(const nsAString& aName)
+  {
+    return NotifyError(aName, EmptyString(), EmptyString(),
+                       NO_ADDITIONAL_INFORMATION, 0 /* ARGC = 0 */);
+  }
+  // non-virtual so it won't affect the vtable
+  inline nsresult NotifyError(const nsAString& aName,
+                              const nsAString& aMessage)
+  {
+    return NotifyError(aName, aMessage, EmptyString(), NO_ADDITIONAL_INFORMATION,
+                       1 /* ARGC = 1 */);
+  }
+  // non-virtual so it won't affect the vtable
+  inline nsresult NotifyError(const nsAString& aName,
+                              const nsAString& aMessage,
+                              const nsAString& aServiceCode)
+  {
+    return NotifyError(aName, aMessage, aServiceCode, NO_ADDITIONAL_INFORMATION,
+                       2 /* ARGC = 2 */);
+  }
+  // non-virtual so it won't affect the vtable
+  inline nsresult NotifyError(const nsAString& aName,
+                              const nsAString& aMessage,
+                              const nsAString& aServiceCode,
+                              uint16_t aAdditionInformation)
+  {
+    return NotifyError(aName, aMessage, aServiceCode, aAdditionInformation,
+                       3 /* ARGC = 3 */);
+  }
+%}
+
+};
+
+%{C++
+#define NS_MOBILE_CONNECTION_SERVICE_CID \
+  { 0xc6f229d4, 0x16e2, 0x4600, \
+    { 0x87, 0x2a, 0x3d, 0x3d, 0xc5, 0xb8, 0x55, 0x41 } }
+#define NS_MOBILE_CONNECTION_SERVICE_CONTRACTID \
+  "@mozilla.org/mobileconnection/mobileconnectionservice;1"
+%}
+
 /**
  * XPCOM component (in the content process) that provides the mobile
  * network information.
  */
-[scriptable, uuid(2a3af80f-9f8e-447d-becd-034f95e4cd4d)]
-interface nsIMobileConnectionProvider : nsISupports
+[scriptable, uuid(b50ad32d-f70e-4729-a947-e8cfdb6ba81f)]
+interface nsIMobileConnectionService : nsISupports
 {
   const long ICC_SERVICE_CLASS_VOICE      = (1 << 0);
   const long ICC_SERVICE_CLASS_DATA       = (1 << 1);
   const long ICC_SERVICE_CLASS_FAX        = (1 << 2);
   const long ICC_SERVICE_CLASS_SMS        = (1 << 3);
   const long ICC_SERVICE_CLASS_DATA_SYNC  = (1 << 4);
   const long ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5);
   const long ICC_SERVICE_CLASS_PACKET     = (1 << 6);
@@ -164,20 +258,20 @@ interface nsIMobileConnectionProvider : 
   const long CLIR_INVOCATION  = 1;
   const long CLIR_SUPPRESSION = 2;
 
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'mobileconnection' permission is allowed to register.
    */
-  void registerMobileConnectionMsg(in unsigned long clientId,
-                                   in nsIMobileConnectionListener listener);
-  void unregisterMobileConnectionMsg(in unsigned long clientId,
-                                     in nsIMobileConnectionListener listener);
+  void registerListener(in unsigned long clientId,
+                        in nsIMobileConnectionListener listener);
+  void unregisterListener(in unsigned long clientId,
+                          in nsIMobileConnectionListener listener);
 
   /**
    * These two fields require the 'mobilenetwork' permission.
    */
   DOMString getLastKnownNetwork(in unsigned long clientId);
   DOMString getLastKnownHomeNetwork(in unsigned long clientId);
 
   /**
@@ -248,502 +342,451 @@ interface nsIMobileConnectionProvider : 
    */
   nsIVariant getSupportedNetworkTypes(in unsigned long clientId);
 
   /**
    * Search for available networks.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called. And the request's
+   * If successful, the notifyGetNetworksSuccess() will be called. And the
    * result will be an array of nsIMobileNetworkInfo.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getNetworks(in unsigned long clientId,
-                               in nsIDOMWindow window);
+  void getNetworks(in unsigned long clientId,
+                   in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Manually selects the passed in network, overriding the radio's current
    * selection.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param network
    *        The manually selecting network.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest selectNetwork(in unsigned long clientId,
-                                 in nsIDOMWindow window,
-                                 in nsIMobileNetworkInfo network);
+  void selectNetwork(in unsigned long clientId,
+                     in nsIMobileNetworkInfo network,
+                     in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Tell the radio to automatically select a network.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest selectNetworkAutomatically(in unsigned long clientId,
-                                              in nsIDOMWindow window);
+  void selectNetworkAutomatically(in unsigned long clientId,
+                                  in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set preferred network type.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param type
    *        DOMString indicates the desired preferred network type.
    *        Possible values: 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto',
    *        'cdma/evdo', 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo',
    *        'lte/cdma/evdo', 'lte/wcdma/gsm', 'lte/wcdma/gsm/cdma/evdo' or
    *        'lte'.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest setPreferredNetworkType(in unsigned long clientId,
-                                           in nsIDOMWindow window,
-                                           in DOMString type);
+  void setPreferredNetworkType(in unsigned long clientId,
+                               in DOMString type,
+                               in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current preferred network type.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
-   *
-   * @return a nsIDOMDOMRequest
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be a string indicating the current preferred network type.
-   * The value will be either 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto',
-   * 'cdma/evdo', 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo', 'lte/cdma/evdo',
-   * 'lte/wcdma/gsm', 'lte/wcdma/gsm/cdma/evdo' or 'lte'.
+   * If successful, the notifySuccessString() will be called. And the result
+   * will be a string indicating the current preferred network type. The value
+   * will be either 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto', 'cdma/evdo',
+   * 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo', 'lte/cdma/evdo', 'lte/wcdma/gsm',
+   * 'lte/wcdma/gsm/cdma/evdo' or 'lte'.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getPreferredNetworkType(in unsigned long clientId,
-                                           in nsIDOMWindow window);
+  void getPreferredNetworkType(in unsigned long clientId,
+                               in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set roaming preference.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param mode
    *        DOMString indicates the desired roaming preference.
    *        Possible values: 'home', 'affiliated', or 'any'.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest setRoamingPreference(in unsigned long clientId,
-                                        in nsIDOMWindow window,
-                                        in DOMString mode);
+  void setRoamingPreference(in unsigned long clientId,
+                            in DOMString mode,
+                            in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current roaming preference.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
+   * If successful, the notifySuccessWithString() will be called. And the result
+   * will be a string indicating the current roaming preference. The value will
+   * be either 'home', 'affiliated', or 'any'.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be a string indicating the current roaming preference.
-   * The value will be either 'home', 'affiliated', or 'any'.
-   *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getRoamingPreference(in unsigned long clientId,
-                                        in nsIDOMWindow window);
+  void getRoamingPreference(in unsigned long clientId,
+                            in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set voice privacy preference.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param enabled
    *        Boolean indicates the preferred voice privacy mode used in voice
    *        scrambling in CDMA networks. 'True' means the enhanced voice security
    *        is required.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest setVoicePrivacyMode(in unsigned long clientId,
-                                       in nsIDOMWindow window,
-                                       in bool enabled);
+  void setVoicePrivacyMode(in unsigned long clientId,
+                           in bool enabled,
+                           in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Query current voice privacy mode.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
+   * If successful, the notifySuccessWithBoolean() will be called. And the result
+   * will be a boolean indicating the current voice privacy mode.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be a boolean indicating the current voice privacy mode.
-   *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getVoicePrivacyMode(in unsigned long clientId,
-                                       in nsIDOMWindow window);
+  void getVoicePrivacyMode(in unsigned long clientId,
+                           in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Send a MMI message.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param mmi
    *        DOMString containing an MMI string that can be associated to a
    *        USSD request or other RIL functionality.
-   *
-   * @return a nsIDOMDOMRequest
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be an object containing information about the operation.
-   * @see MozMMIResult for the detail of result.
+   * If successful, the notifySendCancelMmiSuccess() will be called. And the
+   * result will contain the information about the mmi operation.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be a DOMMMIError.
-   * @see DOMMMIError for the detail of error.
+   * Otherwise, the notifyError() will be called.
    */
-  nsIDOMDOMRequest sendMMI(in unsigned long clientId,
-                           in nsIDOMWindow window,
-                           in DOMString mmi);
+  void sendMMI(in unsigned long clientId,
+               in DOMString mmi,
+               in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Cancel the current MMI request if one exists.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
-   *
-   * @return a nsIDOMDOMRequest
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be an object containing information about the operation.
-   * @see MozMMIResult for the detail of result.
+   * If successful, the notifySendCancelMmiSuccess() will be called. And the
+   * result will contain the information about the mmi operation.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be a DOMMMIError.
-   * @see DOMMMIError for the detail of error.
+   * Otherwise, the notifyError() will be called.
    */
-  nsIDOMDOMRequest cancelMMI(in unsigned long clientId,
-                             in nsIDOMWindow window);
+  void cancelMMI(in unsigned long clientId,
+                 in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call forwarding options.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param reason
    *        Indicates the reason the call is being forwarded. It shall be one of
-   *        the nsIMobileConnectionProvider.CALL_FORWARD_REASON_* values.
+   *        the nsIMobileConnectionService.CALL_FORWARD_REASON_* values.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called. And the request's
+   * If successful, the notifyGetCallForwardingSuccess() will be called. And the
    * result will be an array of MozCallForwardingOptions.
    * @see MozCallForwardingOptions for the detail of result.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest getCallForwarding(in unsigned long clientId,
-                                     in nsIDOMWindow window,
-                                     in unsigned short reason);
+  void getCallForwarding(in unsigned long clientId,
+                         in unsigned short reason,
+                         in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call forwarding options.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param options
    *        An object containing the call forward rule to set.
    * @see MozCallForwardingOptions for the detail of options.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest setCallForwarding(in unsigned long clientId,
-                                     in nsIDOMWindow window,
-                                     in jsval options);
+  void setCallForwarding(in unsigned long clientId,
+                         in jsval options,
+                         in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call barring status.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param options
    *        An object containing the call barring rule to query. No need to
    *        specify 'enabled' property.
    * @see MozCallBarringOptions for the detail of options.
-   *
-   * @return a nsIDOMDOMRequest
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be an object of MozCallBarringOptions with correct 'enabled'
-   * property indicating the status of this rule.
-   * @see MozCallBarringOptions for the detail of result.
+   * If successful, the notifyGetCallBarringSuccess() will be called. And the
+   * result will contain correct 'enabled' property indicating the status of
+   * this rule.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest getCallBarring(in unsigned long clientId,
-                                  in nsIDOMWindow window,
-                                  in jsval options);
+  void getCallBarring(in unsigned long clientId,
+                      in jsval options,
+                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call barring option.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param options
    *        An object containing the call barring rule to set.
    * @see MozCallBarringOptions for the detail of options.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest setCallBarring(in unsigned long clientId,
-                                  in nsIDOMWindow window,
-                                  in jsval options);
+  void setCallBarring(in unsigned long clientId,
+                      in jsval options,
+                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Change call barring facility password.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param options
    *        An object containing information about pin and newPin, and,
    *        this object must have both "pin" and "newPin" attributes
    *        to change the call barring facility password.
    * @see MozCallBarringOptions for the detail of options.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest changeCallBarringPassword(in unsigned long clientId,
-                                             in nsIDOMWindow window,
-                                             in jsval options);
+  void changeCallBarringPassword(in unsigned long clientId,
+                                 in jsval options,
+                                 in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Configures call waiting options.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param enabled
    *        Boolean indicates the desired call waiting status.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest setCallWaiting(in unsigned long clientId,
-                                  in nsIDOMWindow window,
-                                  in bool enabled);
+  void setCallWaiting(in unsigned long clientId,
+                      in bool enabled,
+                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current call waiting options.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
+   * If successful, the notifySuccessWithBoolean() will be called. And the result
+   * will be a boolean indicating the call waiting status.
    *
-   * If successful, the request's onsuccess will be called. And the request's
-   * result will be a boolean indicating the call waiting status.
-   *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getCallWaiting(in unsigned long clientId,
-                                  in nsIDOMWindow window);
+  void getCallWaiting(in unsigned long clientId,
+                      in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Enables or disables the presentation of the calling line identity (CLI) to
    * the called party when originating a call.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param clirMode
-   *        One of the nsIMobileConnectionProvider.CLIR_* values.
+   *        One of the nsIMobileConnectionService.CLIR_* values.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
+   * If successful, the notifySuccess() will be called.
    *
-   * If successful, the request's onsuccess will be called.
-   *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'InvalidParameter', 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter',
+   * 'IllegalSIMorME', or 'GenericFailure'.
    */
-  nsIDOMDOMRequest setCallingLineIdRestriction(in unsigned long clientId,
-                                               in nsIDOMWindow window,
-                                               in unsigned short clirMode);
+  void setCallingLineIdRestriction(in unsigned long clientId,
+                                   in unsigned short clirMode,
+                                   in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Queries current CLIR status.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called. And the request's
+   * If successful, the notifyGetClirStatusSuccess() will be called. And the
    * result will be a an object containing CLIR 'n' and 'm' parameter.
    * @see MozClirStatus for the detail of result.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest getCallingLineIdRestriction(in unsigned long clientId,
-                                               in nsIDOMWindow window);
+  void getCallingLineIdRestriction(in unsigned long clientId,
+                                   in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Exit emergency callback mode.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * @return a nsIDOMDOMRequest
-   *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'RadioNotAvailable', 'RequestNotSupported',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    */
-  nsIDOMDOMRequest exitEmergencyCbMode(in unsigned long clientId,
-                                       in nsIDOMWindow window);
+  void exitEmergencyCbMode(in unsigned long clientId,
+                           in nsIMobileConnectionCallback requestCallback);
 
   /**
    * Set radio enabled/disabled.
    *
    * @param clientId
    *        Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param window
-   *        Current window.
    * @param enabled
    *        Boolean indicates the desired radio power. True to enable the radio.
-   *
-   * @return a nsIDOMDOMRequest
+   * @param requestCallback
+   *        Called when request is finished.
    *
-   * If successful, the request's onsuccess will be called.
+   * If successful, the notifySuccess() will be called.
    *
-   * Otherwise, the request's onerror will be called, and the request's error
-   * will be either 'InvalidStateError', 'RadioNotAvailable',
-   * 'IllegalSIMorME', or 'GenericFailure'.
+   * Otherwise, the notifyError() will be called, and the error will be either
+   * 'InvalidStateError', 'RadioNotAvailable', 'IllegalSIMorME', or
+   * 'GenericFailure'.
    *
    * Note: Request is not available when radioState is null, 'enabling', or
    * 'disabling'. Calling the function in above conditions will receive
    * 'InvalidStateError' error.
    */
-  nsIDOMDOMRequest setRadioEnabled(in unsigned long clientId,
-                                   in nsIDOMWindow window,
-                                   in bool enabled);
+  void setRadioEnabled(in unsigned long clientId,
+                       in bool enabled,
+                       in nsIMobileConnectionCallback requestCallback);
 };
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.cpp
@@ -0,0 +1,484 @@
+/* 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 "MobileConnectionChild.h"
+
+#include "MobileConnectionCallback.h"
+#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobileconnection;
+
+void
+MobileConnectionChild::Init()
+{
+  nsIMobileConnectionInfo* rawVoice;
+  nsIMobileConnectionInfo* rawData;
+  nsTArray<nsString> types;
+
+  SendInit(&rawVoice, &rawData, &mLastNetwork, &mLastHomeNetwork, &mIccId,
+           &mNetworkSelectionMode, &mRadioState, &types);
+
+  // Use dont_AddRef here because this instances is already AddRef-ed in
+  // MobileConnectionIPCSerializer.h
+  nsCOMPtr<nsIMobileConnectionInfo> voice = dont_AddRef(rawVoice);
+  mVoice = new MobileConnectionInfo(nullptr);
+  mVoice->Update(voice);
+
+  // Use dont_AddRef here because this instances is already AddRef-ed in
+  // MobileConnectionIPCSerializer.h
+  nsCOMPtr<nsIMobileConnectionInfo> data = dont_AddRef(rawData);
+  mData = new MobileConnectionInfo(nullptr);
+  mData->Update(data);
+
+
+  // Initial SupportedNetworkTypes
+  nsresult rv;
+  mSupportedNetworkTypes = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  uint32_t arrayLen = types.Length();
+  if (arrayLen == 0) {
+    mSupportedNetworkTypes->SetAsEmptyArray();
+  } else {
+    // Note: The resulting nsIVariant dupes both the array and its elements.
+    const char16_t** array = reinterpret_cast<const char16_t**>
+                               (NS_Alloc(arrayLen * sizeof(const char16_t***)));
+    if (array) {
+      for (uint32_t i = 0; i < arrayLen; ++i) {
+        array[i] = types[i].get();
+      }
+
+      mSupportedNetworkTypes->SetAsArray(nsIDataType::VTYPE_WCHAR_STR,
+                                         nullptr,
+                                         arrayLen,
+                                         reinterpret_cast<void*>(array));
+      NS_Free(array);
+    }
+  }
+}
+
+void
+MobileConnectionChild::Shutdown()
+{
+  if (mLive) {
+    mLive = false;
+    Send__delete__(this);
+  }
+
+  mListeners.Clear();
+  mVoice = nullptr;
+  mData = nullptr;
+  mSupportedNetworkTypes = nullptr;
+}
+
+void
+MobileConnectionChild::RegisterListener(nsIMobileConnectionListener* aListener)
+{
+  if (!mListeners.Contains(aListener)) {
+    mListeners.AppendObject(aListener);
+  }
+}
+
+void
+MobileConnectionChild::UnregisterListener(nsIMobileConnectionListener* aListener)
+{
+  mListeners.RemoveObject(aListener);
+}
+
+MobileConnectionInfo*
+MobileConnectionChild::GetVoiceInfo()
+{
+  return mVoice;
+}
+
+MobileConnectionInfo*
+MobileConnectionChild::GetDataInfo()
+{
+  return mData;
+}
+
+void
+MobileConnectionChild::GetIccId(nsAString& aIccId)
+{
+  aIccId = mIccId;
+}
+
+void
+MobileConnectionChild::GetRadioState(nsAString& aRadioState)
+{
+  aRadioState = mRadioState;
+}
+
+nsIVariant*
+MobileConnectionChild::GetSupportedNetworkTypes()
+{
+  return mSupportedNetworkTypes;
+}
+
+void
+MobileConnectionChild::GetLastNetwork(nsAString& aNetwork)
+{
+  aNetwork = mLastNetwork;
+}
+
+void
+MobileConnectionChild::GetLastHomeNetwork(nsAString& aNetwork)
+{
+  aNetwork = mLastHomeNetwork;
+}
+
+void
+MobileConnectionChild::GetNetworkSelectionMode(nsAString& aMode)
+{
+  aMode = mNetworkSelectionMode;
+}
+
+bool
+MobileConnectionChild::SendRequest(MobileConnectionRequest aRequest,
+                                   nsIMobileConnectionCallback* aRequestCallback)
+{
+  NS_ENSURE_TRUE(mLive, false);
+
+  // Deallocated in MobileConnectionChild::DeallocPMobileConnectionRequestChild().
+  MobileConnectionRequestChild* actor = new MobileConnectionRequestChild(aRequestCallback);
+  SendPMobileConnectionRequestConstructor(actor, aRequest);
+
+  return true;
+}
+
+void
+MobileConnectionChild::ActorDestroy(ActorDestroyReason why)
+{
+  mLive = false;
+}
+
+PMobileConnectionRequestChild*
+MobileConnectionChild::AllocPMobileConnectionRequestChild(const MobileConnectionRequest& request)
+{
+  MOZ_CRASH("Caller is supposed to manually construct a request!");
+}
+
+bool
+MobileConnectionChild::DeallocPMobileConnectionRequestChild(PMobileConnectionRequestChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyVoiceInfoChanged(nsIMobileConnectionInfo* const& aInfo)
+{
+  // Use dont_AddRef here because this instances is already AddRef-ed in
+  // MobileConnectionIPCSerializer.h
+  nsCOMPtr<nsIMobileConnectionInfo> voice = dont_AddRef(aInfo);
+  mVoice->Update(voice);
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyVoiceChanged();
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyDataInfoChanged(nsIMobileConnectionInfo* const& aInfo)
+{
+  // Use dont_AddRef here because this instances is already AddRef-ed in
+  // MobileConnectionIPCSerializer.h
+  nsCOMPtr<nsIMobileConnectionInfo> data = dont_AddRef(aInfo);
+  mData->Update(data);
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyDataChanged();
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyUssdReceived(const nsString& aMessage,
+                                              const bool& aSessionEnd)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyUssdReceived(aMessage, aSessionEnd);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyDataError(const nsString& aMessage)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyDataError(aMessage);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyCFStateChanged(const bool& aSuccess,
+                                                const uint16_t& aAction,
+                                                const uint16_t& aReason,
+                                                const nsString& aNumber,
+                                                const uint16_t& aTimeSeconds,
+                                                const uint16_t& aServiceClass)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyCFStateChanged(aSuccess, aAction, aReason, aNumber,
+                                        aTimeSeconds, aServiceClass);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyEmergencyCbModeChanged(const bool& aActive,
+                                                        const uint32_t& aTimeoutMs)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyEmergencyCbModeChanged(aActive, aTimeoutMs);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyOtaStatusChanged(const nsString& aStatus)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyOtaStatusChanged(aStatus);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyIccChanged(const nsString& aIccId)
+{
+  mIccId.Assign(aIccId);
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyIccChanged();
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyRadioStateChanged(const nsString& aRadioState)
+{
+  mRadioState.Assign(aRadioState);
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyRadioStateChanged();
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyClirModeChanged(const uint32_t& aMode)
+{
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyClirModeChanged(aMode);
+  }
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyLastNetworkChanged(const nsString& aNetwork)
+{
+  mLastNetwork.Assign(aNetwork);
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyLastHomeNetworkChanged(const nsString& aNetwork)
+{
+  mLastHomeNetwork.Assign(aNetwork);
+
+  return true;
+}
+
+bool
+MobileConnectionChild::RecvNotifyNetworkSelectionModeChanged(const nsString& aMode)
+{
+  mNetworkSelectionMode.Assign(aMode);
+
+  return true;
+}
+
+/******************************************************************************
+ * MobileConnectionRequestChild
+ ******************************************************************************/
+
+void
+MobileConnectionRequestChild::ActorDestroy(ActorDestroyReason why)
+{
+  mRequestCallback = nullptr;
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccess& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifySuccess());
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessString& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifySuccessWithString(aReply.result()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessBoolean& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifySuccessWithBoolean(aReply.result()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessNetworks& aReply)
+{
+  uint32_t count = aReply.results().Length();
+  nsTArray<nsCOMPtr<nsIMobileNetworkInfo>> results;
+  for (uint32_t i = 0; i < count; i++) {
+    // Use dont_AddRef here because these instances are already AddRef-ed in
+    // MobileConnectionIPCSerializer.h
+    nsCOMPtr<nsIMobileNetworkInfo> item = dont_AddRef(aReply.results()[i]);
+    results.AppendElement(item);
+  }
+
+  return NS_SUCCEEDED(mRequestCallback->NotifyGetNetworksSuccess(count,
+                                                                 const_cast<nsIMobileNetworkInfo**>(aReply.results().Elements())));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessMmi& aReply)
+{
+  nsAutoString serviceCode(aReply.serviceCode());
+  nsAutoString statusMessage(aReply.statusMessage());
+  AdditionalInformation info(aReply.additionalInformation());
+
+  nsRefPtr<MobileConnectionCallback> callback = static_cast<MobileConnectionCallback*>(mRequestCallback.get());
+
+
+  // Handle union types
+  switch (info.type()) {
+    case AdditionalInformation::Tvoid_t:
+      return NS_SUCCEEDED(callback->NotifySendCancelMmiSuccess(serviceCode,
+                                                               statusMessage));
+    case AdditionalInformation::Tuint16_t:
+      return NS_SUCCEEDED(callback->NotifySendCancelMmiSuccess(serviceCode,
+                                                               statusMessage,
+                                                               info.get_uint16_t()));
+    case AdditionalInformation::TArrayOfnsString:
+      return NS_SUCCEEDED(callback->NotifySendCancelMmiSuccess(serviceCode,
+                                                               statusMessage,
+                                                               info.get_ArrayOfnsString()));
+    case AdditionalInformation::TArrayOfMozCallForwardingOptions:
+      return NS_SUCCEEDED(callback->NotifySendCancelMmiSuccess(serviceCode,
+                                                               statusMessage,
+                                                               info.get_ArrayOfMozCallForwardingOptions()));
+
+    default:
+      MOZ_CRASH("Received invalid type!");
+  }
+
+  return false;
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessCallForwarding& aReply)
+{
+  nsRefPtr<MobileConnectionCallback> callback = static_cast<MobileConnectionCallback*>(mRequestCallback.get());
+  return NS_SUCCEEDED(callback->NotifyGetCallForwardingSuccess(aReply.results()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessCallBarring& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifyGetCallBarringSuccess(aReply.program(),
+                                                                    aReply.enabled(),
+                                                                    aReply.serviceClass()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessClirStatus& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifyGetClirStatusSuccess(aReply.n(),
+                                                                   aReply.m()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplyError& aReply)
+{
+  return NS_SUCCEEDED(mRequestCallback->NotifyError(aReply.message()));
+}
+
+bool
+MobileConnectionRequestChild::DoReply(const MobileConnectionReplyErrorMmi& aReply)
+{
+  nsAutoString name(aReply.name());
+  nsAutoString message(aReply.message());
+  nsAutoString serviceCode(aReply.serviceCode());
+  AdditionalInformation info(aReply.additionalInformation());
+
+  // Handle union types
+  switch (info.type()) {
+    case AdditionalInformation::Tuint16_t:
+      return NS_SUCCEEDED(mRequestCallback->NotifyError(name,
+                                                        message,
+                                                        serviceCode,
+                                                        info.get_uint16_t()));
+    case AdditionalInformation::Tvoid_t:
+    default:
+      // If additionInfomation is not uint16_t, handle it as void_t.
+      return NS_SUCCEEDED(mRequestCallback->NotifyError(name,
+                                                        message,
+                                                        serviceCode));
+  }
+
+  return false;
+}
+
+bool
+MobileConnectionRequestChild::Recv__delete__(const MobileConnectionReply& aReply)
+{
+  MOZ_ASSERT(mRequestCallback);
+
+  switch (aReply.type()) {
+    case MobileConnectionReply::TMobileConnectionReplySuccess:
+      return DoReply(aReply.get_MobileConnectionReplySuccess());
+    case MobileConnectionReply::TMobileConnectionReplySuccessString:
+      return DoReply(aReply.get_MobileConnectionReplySuccessString());
+    case MobileConnectionReply::TMobileConnectionReplySuccessBoolean:
+      return DoReply(aReply.get_MobileConnectionReplySuccessBoolean());
+    case MobileConnectionReply::TMobileConnectionReplySuccessNetworks:
+      return DoReply(aReply.get_MobileConnectionReplySuccessNetworks());
+    case MobileConnectionReply::TMobileConnectionReplySuccessMmi:
+      return DoReply(aReply.get_MobileConnectionReplySuccessMmi());
+    case MobileConnectionReply::TMobileConnectionReplySuccessCallForwarding:
+      return DoReply(aReply.get_MobileConnectionReplySuccessCallForwarding());
+    case MobileConnectionReply::TMobileConnectionReplySuccessCallBarring:
+      return DoReply(aReply.get_MobileConnectionReplySuccessCallBarring());
+    case MobileConnectionReply::TMobileConnectionReplySuccessClirStatus:
+      return DoReply(aReply.get_MobileConnectionReplySuccessClirStatus());
+    case MobileConnectionReply::TMobileConnectionReplyError:
+      return DoReply(aReply.get_MobileConnectionReplyError());
+    case MobileConnectionReply::TMobileConnectionReplyErrorMmi:
+      return DoReply(aReply.get_MobileConnectionReplyErrorMmi());
+    default:
+      MOZ_CRASH("Received invalid response type!");
+  }
+
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.h
@@ -0,0 +1,221 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_mobileconnection_MobileConnectionChild_h
+#define mozilla_dom_mobileconnection_MobileConnectionChild_h
+
+#include "mozilla/dom/MobileConnectionCallback.h"
+#include "mozilla/dom/MobileConnectionInfo.h"
+#include "mozilla/dom/PMobileConnectionChild.h"
+#include "mozilla/dom/PMobileConnectionRequestChild.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsIMobileConnectionService.h"
+#include "nsIVariant.h"
+
+namespace mozilla {
+namespace dom {
+namespace mobileconnection {
+
+/**
+ * Child actor of PMobileConnection. The object is created by
+ * MobileConnectionIPCService and destroyed after MobileConnectionIPCService is
+ * shutdown. For multi-sim device, more than one instance will
+ * be created and each instance represents a sim slot.
+ */
+class MobileConnectionChild : public PMobileConnectionChild
+{
+  NS_INLINE_DECL_REFCOUNTING(MobileConnectionChild)
+
+public:
+  MobileConnectionChild()
+    : mLive(true)
+  {
+    MOZ_COUNT_CTOR(MobileConnectionChild);
+  }
+
+  void
+  Init();
+
+  void
+  Shutdown();
+
+  void
+  RegisterListener(nsIMobileConnectionListener* aListener);
+
+  void
+  UnregisterListener(nsIMobileConnectionListener* aListener);
+
+  MobileConnectionInfo*
+  GetVoiceInfo();
+
+  MobileConnectionInfo*
+  GetDataInfo();
+
+  void
+  GetIccId(nsAString& aIccId);
+
+  void
+  GetRadioState(nsAString& aRadioState);
+
+  nsIVariant*
+  GetSupportedNetworkTypes();
+
+  void
+  GetLastNetwork(nsAString& aNetwork);
+
+  void
+  GetLastHomeNetwork(nsAString& aNetwork);
+
+  void
+  GetNetworkSelectionMode(nsAString& aMode);
+
+  bool
+  SendRequest(MobileConnectionRequest aRequest,
+              nsIMobileConnectionCallback* aRequestCallback);
+
+protected:
+  virtual
+  ~MobileConnectionChild()
+  {
+    MOZ_COUNT_DTOR(MobileConnectionChild);
+    Shutdown();
+  }
+
+  virtual void
+  ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+  virtual PMobileConnectionRequestChild*
+  AllocPMobileConnectionRequestChild(const MobileConnectionRequest& request) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPMobileConnectionRequestChild(PMobileConnectionRequestChild* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyVoiceInfoChanged(nsIMobileConnectionInfo* const& aInfo) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyDataInfoChanged(nsIMobileConnectionInfo* const& aInfo) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyUssdReceived(const nsString& aMessage,
+                         const bool& aSessionEnd) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyDataError(const nsString& aMessage) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyCFStateChanged(const bool& aSuccess, const uint16_t& aAction,
+                           const uint16_t& aReason, const nsString& aNumber,
+                           const uint16_t& aTimeSeconds, const uint16_t& aServiceClass) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyEmergencyCbModeChanged(const bool& aActive,
+                                   const uint32_t& aTimeoutMs) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyOtaStatusChanged(const nsString& aStatus) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyIccChanged(const nsString& aIccId) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyRadioStateChanged(const nsString& aRadioState) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyClirModeChanged(const uint32_t& aMode) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyLastNetworkChanged(const nsString& aNetwork) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyLastHomeNetworkChanged(const nsString& aNetwork) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyNetworkSelectionModeChanged(const nsString& aMode) MOZ_OVERRIDE;
+
+private:
+  bool mLive;
+  nsCOMArray<nsIMobileConnectionListener> mListeners;
+  nsCOMPtr<nsIWritableVariant> mSupportedNetworkTypes;
+  nsRefPtr<MobileConnectionInfo> mVoice;
+  nsRefPtr<MobileConnectionInfo> mData;
+  nsString mIccId;
+  nsString mRadioState;
+  nsString mLastNetwork;
+  nsString mLastHomeNetwork;
+  nsString mNetworkSelectionMode;
+};
+
+/******************************************************************************
+ * PMobileConnectionRequestChild
+ ******************************************************************************/
+
+/**
+ * Child actor of PMobileConnectionRequest. The object is created when an
+ * asynchronous request is made and destroyed after receiving the response sent
+ * by parent actor.
+ */
+class MobileConnectionRequestChild : public PMobileConnectionRequestChild
+{
+public:
+  MobileConnectionRequestChild(nsIMobileConnectionCallback* aRequestCallback)
+    : mRequestCallback(aRequestCallback)
+  {
+    MOZ_COUNT_CTOR(MobileConnectionRequestChild);
+    MOZ_ASSERT(mRequestCallback);
+  }
+
+  bool
+  DoReply(const MobileConnectionReplySuccess& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessString& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessBoolean& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessNetworks& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessMmi& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessCallForwarding& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessCallBarring& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplySuccessClirStatus& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplyError& aReply);
+
+  bool
+  DoReply(const MobileConnectionReplyErrorMmi& aReply);
+
+protected:
+  virtual
+  ~MobileConnectionRequestChild()
+  {
+    MOZ_COUNT_DTOR(MobileConnectionRequestChild);
+  }
+
+  virtual void
+  ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+  virtual bool
+  Recv__delete__(const MobileConnectionReply& aReply) MOZ_OVERRIDE;
+
+private:
+  nsCOMPtr<nsIMobileConnectionCallback> mRequestCallback;
+};
+
+} // namespace mobileconnection
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_mobileconnection_MobileConnectionChild_h
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCSerializer.h
@@ -0,0 +1,749 @@
+/* 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 dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
+#define dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/dom/MobileCellInfo.h"
+#include "mozilla/dom/MobileConnectionInfo.h"
+#include "mozilla/dom/MobileNetworkInfo.h"
+#include "MozMobileConnectionBinding.h"
+
+using mozilla::AutoSafeJSContext;
+using mozilla::dom::MobileNetworkInfo;
+using mozilla::dom::MobileCellInfo;
+using mozilla::dom::MobileConnectionInfo;
+
+typedef nsIMobileCellInfo* nsMobileCellInfo;
+typedef nsIMobileConnectionInfo* nsMobileConnectionInfo;
+typedef nsIMobileNetworkInfo* nsMobileNetworkInfo;
+
+namespace IPC {
+
+struct MozCallForwardingOptions : public mozilla::dom::MozCallForwardingOptions
+{
+  bool operator==(const MozCallForwardingOptions& aOther) const
+  {
+    return // Compare mActive
+           ((!mActive.WasPassed() && !aOther.mActive.WasPassed()) ||
+            (mActive.WasPassed() && aOther.mActive.WasPassed() &&
+             mActive.Value() == aOther.mActive.Value())) &&
+           // Compare mAction
+           ((!mAction.WasPassed() && !aOther.mAction.WasPassed()) ||
+            (mAction.WasPassed() && aOther.mAction.WasPassed() &&
+             mAction.Value() == aOther.mAction.Value())) &&
+           // Compare mReason
+           ((!mReason.WasPassed() && !aOther.mReason.WasPassed()) ||
+            (mReason.WasPassed() && aOther.mReason.WasPassed() &&
+             mReason.Value() == aOther.mReason.Value())) &&
+           // Compare mNumber
+           ((!mNumber.WasPassed() && !aOther.mNumber.WasPassed()) ||
+            (mNumber.WasPassed() && aOther.mNumber.WasPassed() &&
+             mNumber.Value() == aOther.mNumber.Value())) &&
+           // Compare mTimeSeconds
+           ((!mTimeSeconds.WasPassed() && !aOther.mTimeSeconds.WasPassed()) ||
+            (mTimeSeconds.WasPassed() && aOther.mTimeSeconds.WasPassed() &&
+             mTimeSeconds.Value() == aOther.mTimeSeconds.Value())) &&
+           // Compare mServiceClass
+           ((!mServiceClass.WasPassed() && !aOther.mServiceClass.WasPassed()) ||
+            (mServiceClass.WasPassed() && aOther.mServiceClass.WasPassed() &&
+             mServiceClass.Value() == aOther.mServiceClass.Value()));
+  };
+};
+
+struct MozCallBarringOptions : mozilla::dom::MozCallBarringOptions
+{
+  bool operator==(const MozCallBarringOptions& aOther) const
+  {
+    return // Compare mEnabled
+           ((!mEnabled.WasPassed() && !aOther.mEnabled.WasPassed()) ||
+            (mEnabled.WasPassed() && aOther.mEnabled.WasPassed() &&
+             mEnabled.Value() == aOther.mEnabled.Value())) &&
+           // Compare mPassword
+           ((!mPassword.WasPassed() && !aOther.mPassword.WasPassed()) ||
+            (mPassword.WasPassed() && aOther.mPassword.WasPassed() &&
+             mPassword.Value() == aOther.mPassword.Value())) &&
+           // Compare mProgram
+           ((!mProgram.WasPassed() && !aOther.mProgram.WasPassed()) ||
+            (mProgram.WasPassed() && aOther.mProgram.WasPassed() &&
+             mProgram.Value() == aOther.mProgram.Value())) &&
+           // Compare mServiceClass
+           ((!mServiceClass.WasPassed() && !aOther.mServiceClass.WasPassed()) ||
+            (mServiceClass.WasPassed() && aOther.mServiceClass.WasPassed() &&
+             mServiceClass.Value() == aOther.mServiceClass.Value()));
+  };
+};
+
+/**
+ * nsIMobileNetworkInfo Serialize/De-serialize.
+ */
+template <>
+struct ParamTraits<nsIMobileNetworkInfo*>
+{
+  typedef nsIMobileNetworkInfo* paramType;
+
+  // Function to serialize a MobileNetworkInfo.
+  static void Write(Message *aMsg, const paramType& aParam)
+  {
+    bool isNull = !aParam;
+    WriteParam(aMsg, isNull);
+    // If it is a null object, then we are done.
+    if (isNull) {
+      return;
+    }
+
+    nsString pString;
+    aParam->GetShortName(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetLongName(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetMcc(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetMnc(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetState(pString);
+    WriteParam(aMsg, pString);
+
+    // We release the ref here given that ipdl won't handle reference counting.
+    aParam->Release();
+  }
+
+  // Function to de-serialize a MobileNetworkInfo.
+  static bool Read(const Message *aMsg, void **aIter, paramType* aResult)
+  {
+    // Check if is the null pointer we have transfered.
+    bool isNull;
+    if (!ReadParam(aMsg, aIter, &isNull)) {
+      return false;
+    }
+
+    if (isNull) {
+      *aResult = nullptr;
+      return true;
+    }
+
+    nsString shortName;
+    nsString longName;
+    nsString mcc;
+    nsString mnc;
+    nsString state;
+
+    // It's not important to us where it fails, but rather if it fails
+    if (!(ReadParam(aMsg, aIter, &shortName) &&
+          ReadParam(aMsg, aIter, &longName) &&
+          ReadParam(aMsg, aIter, &mcc) &&
+          ReadParam(aMsg, aIter, &mnc) &&
+          ReadParam(aMsg, aIter, &state))) {
+      return false;
+    }
+
+    *aResult = new MobileNetworkInfo(shortName,
+                                     longName,
+                                     mcc,
+                                     mnc,
+                                     state);
+    // We release this ref after receiver finishes processing.
+    NS_ADDREF(*aResult);
+
+    return true;
+  }
+};
+
+/**
+ * nsIMobileCellInfo Serialize/De-serialize.
+ */
+template <>
+struct ParamTraits<nsIMobileCellInfo*>
+{
+  typedef nsIMobileCellInfo* paramType;
+
+  // Function to serialize a MobileCellInfo.
+  static void Write(Message *aMsg, const paramType& aParam)
+  {
+    bool isNull = !aParam;
+    WriteParam(aMsg, isNull);
+    // If it is a null object, then we are done.
+    if (isNull) {
+      return;
+    }
+
+    int32_t pLong;
+    int64_t pLongLong;
+
+    aParam->GetGsmLocationAreaCode(&pLong);
+    WriteParam(aMsg, pLong);
+
+    aParam->GetGsmCellId(&pLongLong);
+    WriteParam(aMsg, pLongLong);
+
+    aParam->GetCdmaBaseStationId(&pLong);
+    WriteParam(aMsg, pLong);
+
+    aParam->GetCdmaBaseStationLatitude(&pLong);
+    WriteParam(aMsg, pLong);
+
+    aParam->GetCdmaBaseStationLongitude(&pLong);
+    WriteParam(aMsg, pLong);
+
+    aParam->GetCdmaSystemId(&pLong);
+    WriteParam(aMsg, pLong);
+
+    aParam->GetCdmaNetworkId(&pLong);
+    WriteParam(aMsg, pLong);
+
+    // We release the ref here given that ipdl won't handle reference counting.
+    aParam->Release();
+  }
+
+  // Function to de-serialize a MobileCellInfo.
+  static bool Read(const Message *aMsg, void **aIter, paramType* aResult)
+  {
+    // Check if is the null pointer we have transfered.
+    bool isNull;
+    if (!ReadParam(aMsg, aIter, &isNull)) {
+      return false;
+    }
+
+    if (isNull) {
+      *aResult = nullptr;
+      return true;
+    }
+
+    int32_t gsmLac;
+    int64_t gsmCellId;
+    int32_t cdmaBsId;
+    int32_t cdmaBsLat;
+    int32_t cdmaBsLong;
+    int32_t cdmaSystemId;
+    int32_t cdmaNetworkId;
+
+    // It's not important to us where it fails, but rather if it fails
+    if (!(ReadParam(aMsg, aIter, &gsmLac) &&
+          ReadParam(aMsg, aIter, &gsmCellId) &&
+          ReadParam(aMsg, aIter, &cdmaBsId) &&
+          ReadParam(aMsg, aIter, &cdmaBsLat) &&
+          ReadParam(aMsg, aIter, &cdmaBsLong) &&
+          ReadParam(aMsg, aIter, &cdmaSystemId) &&
+          ReadParam(aMsg, aIter, &cdmaNetworkId))) {
+      return false;
+    }
+
+    *aResult = new MobileCellInfo(gsmLac, gsmCellId, cdmaBsId, cdmaBsLat,
+                                  cdmaBsLong, cdmaSystemId, cdmaNetworkId);
+    // We release this ref after receiver finishes processing.
+    NS_ADDREF(*aResult);
+
+    return true;
+  }
+};
+
+/**
+ * nsIMobileConnectionInfo Serialize/De-serialize.
+ */
+template <>
+struct ParamTraits<nsIMobileConnectionInfo*>
+{
+  typedef nsIMobileConnectionInfo* paramType;
+
+  // Function to serialize a MobileConnectionInfo.
+  static void Write(Message *aMsg, const paramType& aParam)
+  {
+    bool isNull = !aParam;
+    WriteParam(aMsg, isNull);
+    // If it is a null object, then we are done.
+    if (isNull) {
+      return;
+    }
+
+    AutoSafeJSContext cx;
+    nsString pString;
+    bool pBool;
+    nsCOMPtr<nsIMobileNetworkInfo> pNetworkInfo;
+    nsCOMPtr<nsIMobileCellInfo> pCellInfo;
+    JS::Rooted<JS::Value> pJsval(cx);
+    int32_t pInt32;
+
+    aParam->GetState(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetConnected(&pBool);
+    WriteParam(aMsg, pBool);
+
+    aParam->GetEmergencyCallsOnly(&pBool);
+    WriteParam(aMsg, pBool);
+
+    aParam->GetRoaming(&pBool);
+    WriteParam(aMsg, pBool);
+
+    aParam->GetType(pString);
+    WriteParam(aMsg, pString);
+
+    aParam->GetNetwork(getter_AddRefs(pNetworkInfo));
+    // Release ref when WriteParam is finished.
+    WriteParam(aMsg, pNetworkInfo.forget().take());
+
+    aParam->GetCell(getter_AddRefs(pCellInfo));
+    // Release ref when WriteParam is finished.
+    WriteParam(aMsg, pCellInfo.forget().take());
+
+    // Serialize jsval signalStrength
+    aParam->GetSignalStrength(&pJsval);
+    isNull = !pJsval.isInt32();
+    WriteParam(aMsg, isNull);
+
+    if (!isNull) {
+      pInt32 = pJsval.toInt32();
+      WriteParam(aMsg, pInt32);
+    }
+
+    // Serialize jsval relSignalStrength
+    aParam->GetRelSignalStrength(&pJsval);
+    isNull = !pJsval.isInt32();
+    WriteParam(aMsg, isNull);
+
+    if (!isNull) {
+      pInt32 = pJsval.toInt32();
+      WriteParam(aMsg, pInt32);
+    }
+
+    // We release the ref here given that ipdl won't handle reference counting.
+    aParam->Release();
+  }
+
+  // Function to de-serialize a MobileConectionInfo.
+  static bool Read(const Message* aMsg, void **aIter, paramType* aResult)
+  {
+    // Check if is the null pointer we have transfered.
+    bool isNull;
+    if (!ReadParam(aMsg, aIter, &isNull)) {
+      return false;
+    }
+
+    if (isNull) {
+      *aResult = nullptr;
+      return true;
+    }
+
+    AutoSafeJSContext cx;
+    nsString state;
+    bool connected;
+    bool emergencyOnly;
+    bool roaming;
+    nsString type;
+    nsIMobileNetworkInfo* networkInfo = nullptr;
+    nsIMobileCellInfo* cellInfo = nullptr;
+    Nullable<int32_t> signalStrength;
+    Nullable<uint16_t> relSignalStrength;
+
+    // It's not important to us where it fails, but rather if it fails
+    if (!(ReadParam(aMsg, aIter, &state) &&
+          ReadParam(aMsg, aIter, &connected) &&
+          ReadParam(aMsg, aIter, &emergencyOnly) &&
+          ReadParam(aMsg, aIter, &roaming) &&
+          ReadParam(aMsg, aIter, &type) &&
+          ReadParam(aMsg, aIter, &networkInfo) &&
+          ReadParam(aMsg, aIter, &cellInfo))) {
+      return false;
+    }
+
+    // De-serialize jsval signalStrength
+    if (!ReadParam(aMsg, aIter, &isNull)) {
+      return false;
+    }
+
+    if (!isNull) {
+      int32_t value;
+
+      if (!ReadParam(aMsg, aIter, &value)) {
+        return false;
+      }
+
+      signalStrength.SetValue(value);
+    }
+
+    // De-serialize jsval relSignalStrength
+    if (!ReadParam(aMsg, aIter, &isNull)) {
+      return false;
+    }
+
+    if (!isNull) {
+      int32_t value;
+
+      if (!ReadParam(aMsg, aIter, &value)) {
+        return false;
+      }
+
+      relSignalStrength.SetValue(uint16_t(value));
+    }
+
+    *aResult = new MobileConnectionInfo(state,
+                                        connected,
+                                        emergencyOnly,
+                                        roaming,
+                                        networkInfo,
+                                        type,
+                                        signalStrength,
+                                        relSignalStrength,
+                                        cellInfo);
+    // We release this ref after receiver finishes processing.
+    NS_ADDREF(*aResult);
+    // We already clone the data into MobileConnectionInfo, so release the ref
+    // of networkInfo and cellInfo here.
+    NS_IF_RELEASE(networkInfo);
+    NS_IF_RELEASE(cellInfo);
+
+    return true;
+  }
+};
+
+/**
+ * MozCallForwardingOptions Serialize/De-serialize.
+ */
+template <>
+struct ParamTraits<MozCallForwardingOptions>
+{
+  typedef MozCallForwardingOptions paramType;
+
+  // Function to serialize a MozCallForwardingOptions.
+  static void Write(Message *aMsg, const paramType& aParam)
+  {
+    bool wasPassed = false;
+    bool isNull = false;
+
+    // Write mActive
+    wasPassed = aParam.mActive.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mActive.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mActive.Value().Value());
+      }
+    }
+
+    // Write mAction
+    wasPassed = aParam.mAction.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mAction.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mAction.Value().Value());
+      }
+    }
+
+    // Write mReason
+    wasPassed = aParam.mReason.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mReason.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mReason.Value().Value());
+      }
+    }
+
+    // Write mNumber
+    wasPassed = aParam.mNumber.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      WriteParam(aMsg, aParam.mNumber.Value());
+    }
+
+    // Write mTimeSeconds
+    wasPassed = aParam.mTimeSeconds.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mTimeSeconds.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mTimeSeconds.Value().Value());
+      }
+    }
+
+    // Write mServiceClass
+    wasPassed = aParam.mServiceClass.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mServiceClass.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mServiceClass.Value().Value());
+      }
+    }
+  }
+
+  // Function to de-serialize a MozCallForwardingOptions.
+  static bool Read(const Message *aMsg, void **aIter, paramType* aResult)
+  {
+    bool wasPassed = false;
+    bool isNull = false;
+
+    // Read mActive
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mActive.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mActive.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mAction
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mAction.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mAction.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mReason
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mReason.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mReason.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mNumber
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      if (!ReadParam(aMsg, aIter, &aResult->mNumber.Construct())) {
+        return false;
+      }
+    }
+
+    // Read mTimeSeconds
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mTimeSeconds.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mTimeSeconds.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mServiceClass
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mServiceClass.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mServiceClass.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+};
+
+/**
+ * MozCallBarringOptions Serialize/De-serialize.
+ */
+template <>
+struct ParamTraits<MozCallBarringOptions>
+{
+  typedef MozCallBarringOptions paramType;
+
+  // Function to serialize a MozCallBarringOptions.
+  static void Write(Message *aMsg, const paramType& aParam)
+  {
+    bool wasPassed = false;
+    bool isNull = false;
+
+    // Write mProgram
+    wasPassed = aParam.mProgram.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mProgram.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mProgram.Value().Value());
+      }
+    }
+
+    // Write mEnabled
+    wasPassed = aParam.mEnabled.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mEnabled.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mEnabled.Value().Value());
+      }
+    }
+
+    // Write mPassword
+    wasPassed = aParam.mPassword.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      WriteParam(aMsg, aParam.mPassword.Value());
+    }
+
+    // Write mServiceClass
+    wasPassed = aParam.mServiceClass.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      isNull = aParam.mServiceClass.Value().IsNull();
+      WriteParam(aMsg, isNull);
+      if (!isNull) {
+        WriteParam(aMsg, aParam.mServiceClass.Value().Value());
+      }
+    }
+
+    // Write mPin
+    wasPassed = aParam.mPin.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      WriteParam(aMsg, aParam.mPin.Value());
+    }
+
+    // Write mNewPin
+    wasPassed = aParam.mNewPin.WasPassed();
+    WriteParam(aMsg, wasPassed);
+    if (wasPassed) {
+      WriteParam(aMsg, aParam.mNewPin.Value());
+    }
+  }
+
+  // Function to de-serialize a MozCallBarringOptions.
+  static bool Read(const Message *aMsg, void **aIter, paramType* aResult)
+  {
+    bool wasPassed = false;
+    bool isNull = false;
+
+    // Read mProgram
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mProgram.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mProgram.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mEnabled
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mEnabled.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mEnabled.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mPassword
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      if (!ReadParam(aMsg, aIter, &aResult->mPassword.Construct())) {
+        return false;
+      }
+    }
+
+    // Read mServiceClass
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      aResult->mServiceClass.Construct();
+      if (!ReadParam(aMsg, aIter, &isNull)) {
+        return false;
+      }
+
+      if (!isNull) {
+        if (!ReadParam(aMsg, aIter, &aResult->mServiceClass.Value().SetValue())) {
+          return false;
+        }
+      }
+    }
+
+    // Read mPin
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      if (!ReadParam(aMsg, aIter, &aResult->mPin.Construct())) {
+        return false;
+      }
+    }
+
+    // Read mNewPin
+    if (!ReadParam(aMsg, aIter, &wasPassed)) {
+      return false;
+    }
+    if (wasPassed) {
+      if (!ReadParam(aMsg, aIter, &aResult->mNewPin.Construct())) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+};
+
+} // namespace IPC
+
+#endif // dom_mobileconnection_src_ipc_MobileConnectionIPCSerialiser_h
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCService.cpp
@@ -0,0 +1,394 @@
+/* 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 "MobileConnectionIPCService.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobileconnection;
+
+NS_IMPL_ISUPPORTS(MobileConnectionIPCService, nsIMobileConnectionService)
+
+StaticRefPtr<MobileConnectionIPCService> sService;
+
+/* static */MobileConnectionIPCService*
+MobileConnectionIPCService::GetSingleton()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sService) {
+    return sService;
+  }
+
+  sService = new MobileConnectionIPCService();
+  return sService;
+}
+
+MobileConnectionIPCService::MobileConnectionIPCService()
+{
+  int32_t numRil = Preferences::GetInt("ril.numRadioInterfaces", 1);
+  for (int32_t i = 0; i < numRil; i++) {
+    // Deallocated in ContentChild::DeallocPMobileConnectionChild().
+    nsRefPtr<MobileConnectionChild> client = new MobileConnectionChild();
+    NS_ASSERTION(client, "This shouldn't fail!");
+
+    ContentChild::GetSingleton()->SendPMobileConnectionConstructor(client, i);
+    client->Init();
+
+    mClients.AppendElement(client);
+  }
+}
+
+MobileConnectionIPCService::~MobileConnectionIPCService()
+{
+  uint32_t count = mClients.Length();
+  for (uint32_t i = 0; i < count; i++) {
+    mClients[i]->Shutdown();
+  }
+
+  mClients.Clear();
+}
+
+nsresult
+MobileConnectionIPCService::SendRequest(uint32_t aClientId,
+                                        MobileConnectionRequest aRequest,
+                                        nsIMobileConnectionCallback* aRequestCallback)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->SendRequest(aRequest, aRequestCallback);
+  return NS_OK;
+}
+
+// nsIMobileConnectionService
+
+NS_IMETHODIMP
+MobileConnectionIPCService::RegisterListener(uint32_t aClientId,
+                                             nsIMobileConnectionListener* aListener)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->RegisterListener(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::UnregisterListener(uint32_t aClientId,
+                                               nsIMobileConnectionListener* aListener)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->UnregisterListener(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetLastKnownNetwork(uint32_t aClientId,
+                                                nsAString& aLastNetwork)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetLastNetwork(aLastNetwork);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetLastKnownHomeNetwork(uint32_t aClientId,
+                                                    nsAString& aLastNetwork)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetLastHomeNetwork(aLastNetwork);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetVoiceConnectionInfo(uint32_t aClientId,
+                                                   nsIMobileConnectionInfo** aInfo)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIMobileConnectionInfo> info = mClients[aClientId]->GetVoiceInfo();
+  info.forget(aInfo);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetDataConnectionInfo(uint32_t aClientId,
+                                                  nsIMobileConnectionInfo** aInfo)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIMobileConnectionInfo> info = mClients[aClientId]->GetDataInfo();
+  info.forget(aInfo);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetIccId(uint32_t aClientId, nsAString& aIccId)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetIccId(aIccId);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetNetworkSelectionMode(uint32_t aClientId,
+                                                    nsAString& aNetworkSelectionMode)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetNetworkSelectionMode(aNetworkSelectionMode);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetRadioState(uint32_t aClientId,
+                                          nsAString& aRadioState)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mClients[aClientId]->GetRadioState(aRadioState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetSupportedNetworkTypes(uint32_t aClientId,
+                                                     nsIVariant** aSupportedTypes)
+{
+  if (aClientId >= mClients.Length()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIVariant> supportedTypes = mClients[aClientId]->GetSupportedNetworkTypes();
+  supportedTypes.forget(aSupportedTypes);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetNetworks(uint32_t aClientId,
+                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetNetworksRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SelectNetwork(uint32_t aClientId,
+                                          nsIMobileNetworkInfo* aNetwork,
+                                          nsIMobileConnectionCallback* aRequest)
+{
+  nsCOMPtr<nsIMobileNetworkInfo> network = aNetwork;
+  // We release the ref after serializing process is finished in
+  // MobileConnectionIPCSerializer.
+  return SendRequest(aClientId, SelectNetworkRequest(network.forget().take()), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SelectNetworkAutomatically(uint32_t aClientId,
+                                                       nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SelectNetworkAutoRequest(), aRequest);
+}
+
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetPreferredNetworkType(uint32_t aClientId,
+                                                    const nsAString& aType,
+                                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId,
+                     SetPreferredNetworkTypeRequest(nsAutoString(aType)),
+                     aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetPreferredNetworkType(uint32_t aClientId,
+                                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetPreferredNetworkTypeRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetRoamingPreference(uint32_t aClientId,
+                                                 const nsAString& aMode,
+                                                 nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId,
+                     SetRoamingPreferenceRequest(nsAutoString(aMode)),
+                     aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetRoamingPreference(uint32_t aClientId,
+                                                 nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetRoamingPreferenceRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetVoicePrivacyMode(uint32_t aClientId,
+                                                bool aEnabled,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetVoicePrivacyModeRequest(aEnabled), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetVoicePrivacyMode(uint32_t aClientId,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetVoicePrivacyModeRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SendMMI(uint32_t aClientId,
+                                    const nsAString& aMmi,
+                                    nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SendMmiRequest(nsAutoString(aMmi)), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::CancelMMI(uint32_t aClientId,
+                                      nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, CancelMmiRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallForwarding(uint32_t aClientId,
+                                              JS::Handle<JS::Value> aOptions,
+                                              nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallForwardingOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, SetCallForwardingRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallForwarding(uint32_t aClientId,
+                                              uint16_t aReason,
+                                              nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallForwardingRequest(aReason), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallBarring(uint32_t aClientId,
+                                           JS::Handle<JS::Value> aOptions,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, SetCallBarringRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallBarring(uint32_t aClientId,
+                                           JS::Handle<JS::Value> aOptions,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, GetCallBarringRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::ChangeCallBarringPassword(uint32_t aClientId,
+                                                      JS::Handle<JS::Value> aOptions,
+                                                      nsIMobileConnectionCallback* aRequest)
+{
+  AutoSafeJSContext cx;
+  IPC::MozCallBarringOptions options;
+  if(!options.Init(cx, aOptions)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return SendRequest(aClientId, ChangeCallBarringPasswordRequest(options), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallWaiting(uint32_t aClientId,
+                                           bool aEnabled,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetCallWaitingRequest(aEnabled), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallWaiting(uint32_t aClientId,
+                                           nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallWaitingRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetCallingLineIdRestriction(uint32_t aClientId,
+                                                        uint16_t aMode,
+                                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetCallingLineIdRestrictionRequest(aMode), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::GetCallingLineIdRestriction(uint32_t aClientId,
+                                                        nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, GetCallingLineIdRestrictionRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::ExitEmergencyCbMode(uint32_t aClientId,
+                                                nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, ExitEmergencyCbModeRequest(), aRequest);
+}
+
+NS_IMETHODIMP
+MobileConnectionIPCService::SetRadioEnabled(uint32_t aClientId,
+                                            bool aEnabled,
+                                            nsIMobileConnectionCallback* aRequest)
+{
+  return SendRequest(aClientId, SetRadioEnabledRequest(aEnabled), aRequest);
+}
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionIPCService.h
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_mobileconnection_MobileConnectionIPCService_h
+#define mozilla_dom_mobileconnection_MobileConnectionIPCService_h
+
+#include "nsCOMPtr.h"
+#include "MobileConnectionChild.h"
+#include "nsIMobileConnectionService.h"
+
+namespace mozilla {
+namespace dom {
+namespace mobileconnection {
+
+class MobileConnectionIPCService MOZ_FINAL : public nsIMobileConnectionService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMOBILECONNECTIONSERVICE
+
+  static MobileConnectionIPCService*
+  GetSingleton();
+
+private:
+  MobileConnectionIPCService();
+
+  ~MobileConnectionIPCService();
+
+  /** Send request */
+  nsresult
+  SendRequest(uint32_t aClientId, MobileConnectionRequest aRequest,
+              nsIMobileConnectionCallback* aRequestCallback);
+
+  nsTArray<nsRefPtr<MobileConnectionChild>> mClients;
+};
+
+} // name space mobileconnection
+} // name space dom
+} // name space mozilla
+
+#endif // mozilla_dom_mobileconnection_MobileConnectionIPCService_h
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/ipc/MobileConnectionParent.cpp
@@ -0,0 +1,729 @@
+/* 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 "MobileConnectionParent.h"
+
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/MobileConnectionIPCSerializer.h"
+#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsIVariant.h"
+#include "nsJSUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobileconnection;
+
+MobileConnectionParent::MobileConnectionParent(uint32_t aClientId)
+  : mClientId(aClientId)
+  , mLive(true)
+{
+  MOZ_COUNT_CTOR(MobileConnectionParent);
+
+  mService = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+  NS_ASSERTION(mService, "This shouldn't fail!");
+
+  if (mService) {
+    mService->RegisterListener(mClientId, this);
+  }
+}
+
+void
+MobileConnectionParent::ActorDestroy(ActorDestroyReason why)
+{
+  mLive = false;
+  if (mService) {
+    mService->UnregisterListener(mClientId, this);
+    mService = nullptr;
+  }
+}
+
+bool
+MobileConnectionParent::RecvPMobileConnectionRequestConstructor(PMobileConnectionRequestParent* aActor,
+                                                                const MobileConnectionRequest& aRequest)
+{
+  MobileConnectionRequestParent* actor = static_cast<MobileConnectionRequestParent*>(aActor);
+
+  switch (aRequest.type()) {
+    case MobileConnectionRequest::TGetNetworksRequest:
+      return actor->DoRequest(aRequest.get_GetNetworksRequest());
+    case MobileConnectionRequest::TSelectNetworkRequest:
+      return actor->DoRequest(aRequest.get_SelectNetworkRequest());
+    case MobileConnectionRequest::TSelectNetworkAutoRequest:
+      return actor->DoRequest(aRequest.get_SelectNetworkAutoRequest());
+    case MobileConnectionRequest::TSetPreferredNetworkTypeRequest:
+      return actor->DoRequest(aRequest.get_SetPreferredNetworkTypeRequest());
+    case MobileConnectionRequest::TGetPreferredNetworkTypeRequest:
+      return actor->DoRequest(aRequest.get_GetPreferredNetworkTypeRequest());
+    case MobileConnectionRequest::TSetRoamingPreferenceRequest:
+      return actor->DoRequest(aRequest.get_SetRoamingPreferenceRequest());
+    case MobileConnectionRequest::TGetRoamingPreferenceRequest:
+      return actor->DoRequest(aRequest.get_GetRoamingPreferenceRequest());
+    case MobileConnectionRequest::TSetVoicePrivacyModeRequest:
+      return actor->DoRequest(aRequest.get_SetVoicePrivacyModeRequest());
+    case MobileConnectionRequest::TGetVoicePrivacyModeRequest:
+      return actor->DoRequest(aRequest.get_GetVoicePrivacyModeRequest());
+    case MobileConnectionRequest::TSendMmiRequest:
+      return actor->DoRequest(aRequest.get_SendMmiRequest());
+    case MobileConnectionRequest::TCancelMmiRequest:
+      return actor->DoRequest(aRequest.get_CancelMmiRequest());
+    case MobileConnectionRequest::TSetCallForwardingRequest:
+      return actor->DoRequest(aRequest.get_SetCallForwardingRequest());
+    case MobileConnectionRequest::TGetCallForwardingRequest:
+      return actor->DoRequest(aRequest.get_GetCallForwardingRequest());
+    case MobileConnectionRequest::TSetCallBarringRequest:
+      return actor->DoRequest(aRequest.get_SetCallBarringRequest());
+    case MobileConnectionRequest::TGetCallBarringRequest:
+      return actor->DoRequest(aRequest.get_GetCallBarringRequest());
+    case MobileConnectionRequest::TChangeCallBarringPasswordRequest:
+      return actor->DoRequest(aRequest.get_ChangeCallBarringPasswordRequest());
+    case MobileConnectionRequest::TSetCallWaitingRequest:
+      return actor->DoRequest(aRequest.get_SetCallWaitingRequest());
+    case MobileConnectionRequest::TGetCallWaitingRequest:
+      return actor->DoRequest(aRequest.get_GetCallWaitingRequest());
+    case MobileConnectionRequest::TSetCallingLineIdRestrictionRequest:
+      return actor->DoRequest(aRequest.get_SetCallingLineIdRestrictionRequest());
+    case MobileConnectionRequest::TGetCallingLineIdRestrictionRequest:
+      return actor->DoRequest(aRequest.get_GetCallingLineIdRestrictionRequest());
+    case MobileConnectionRequest::TExitEmergencyCbModeRequest:
+      return actor->DoRequest(aRequest.get_ExitEmergencyCbModeRequest());
+    case MobileConnectionRequest::TSetRadioEnabledRequest:
+      return actor->DoRequest(aRequest.get_SetRadioEnabledRequest());
+    default:
+      MOZ_CRASH("Received invalid request type!");
+  }
+
+  return false;
+}
+
+PMobileConnectionRequestParent*
+MobileConnectionParent::AllocPMobileConnectionRequestParent(const MobileConnectionRequest& request)
+{
+  if (!AssertAppProcessPermission(Manager(), "mobileconnection")) {
+    return nullptr;
+  }
+
+  MobileConnectionRequestParent* actor = new MobileConnectionRequestParent(mClientId);
+  // Add an extra ref for IPDL. Will be released in
+  // MobileConnectionParent::DeallocPMobileConnectionRequestParent().
+  actor->AddRef();
+  return actor;
+}
+
+bool
+MobileConnectionParent::DeallocPMobileConnectionRequestParent(PMobileConnectionRequestParent* aActor)
+{
+  // MobileConnectionRequestParent is refcounted, must not be freed manually.
+  static_cast<MobileConnectionRequestParent*>(aActor)->Release();
+  return true;
+}
+
+bool
+MobileConnectionParent::RecvInit(nsMobileConnectionInfo* aVoice,
+                                 nsMobileConnectionInfo* aData,
+                                 nsString* aLastKnownNetwork,
+                                 nsString* aLastKnownHomeNetwork,
+                                 nsString* aIccId,
+                                 nsString* aNetworkSelectionMode,
+                                 nsString* aRadioState,
+                                 nsTArray<nsString>* aSupportedNetworkTypes)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  NS_ENSURE_SUCCESS(mService->GetVoiceConnectionInfo(mClientId, aVoice), false);
+  NS_ENSURE_SUCCESS(mService->GetDataConnectionInfo(mClientId, aData), false);
+  NS_ENSURE_SUCCESS(mService->GetLastKnownNetwork(mClientId, *aLastKnownNetwork), false);
+  NS_ENSURE_SUCCESS(mService->GetLastKnownHomeNetwork(mClientId, *aLastKnownHomeNetwork), false);
+  NS_ENSURE_SUCCESS(mService->GetIccId(mClientId, *aIccId), false);
+  NS_ENSURE_SUCCESS(mService->GetNetworkSelectionMode(mClientId, *aNetworkSelectionMode), false);
+  NS_ENSURE_SUCCESS(mService->GetRadioState(mClientId, *aRadioState), false);
+
+  nsCOMPtr<nsIVariant> variant;
+  mService->GetSupportedNetworkTypes(mClientId, getter_AddRefs(variant));
+
+  uint16_t type;
+  nsIID iid;
+  uint32_t count;
+  void* data;
+  if (NS_FAILED(variant->GetAsArray(&type, &iid, &count, &data))) {
+    return false;
+  }
+
+  // We expect the element type is wstring.
+  if (type == nsIDataType::VTYPE_WCHAR_STR) {
+    char16_t** rawArray = reinterpret_cast<char16_t**>(data);
+    for (uint32_t i = 0; i < count; ++i) {
+      nsDependentString networkType(rawArray[i]);
+      aSupportedNetworkTypes->AppendElement(networkType);
+    }
+  }
+  NS_Free(data);
+
+  return true;
+}
+
+// nsIMobileConnectionListener
+
+NS_IMPL_ISUPPORTS(MobileConnectionParent, nsIMobileConnectionListener)
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyVoiceChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsCOMPtr<nsIMobileConnectionInfo> info;
+  rv = mService->GetVoiceConnectionInfo(mClientId, getter_AddRefs(info));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We release the ref after serializing process is finished in
+  // MobileConnectionIPCSerializer.
+  return SendNotifyVoiceInfoChanged(info.forget().take()) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyDataChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsCOMPtr<nsIMobileConnectionInfo> info;
+  rv = mService->GetDataConnectionInfo(mClientId, getter_AddRefs(info));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We release the ref after serializing process is finished in
+  // MobileConnectionIPCSerializer.
+  return SendNotifyDataInfoChanged(info.forget().take()) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyUssdReceived(const nsAString& aMessage,
+                                           bool aSessionEnded)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyUssdReceived(nsAutoString(aMessage), aSessionEnded)
+         ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyDataError(const nsAString& aMessage)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyDataError(nsAutoString(aMessage)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyCFStateChanged(bool aSuccess,
+                                             uint16_t aAction,
+                                             uint16_t aReason,
+                                             const nsAString &aNumber,
+                                             uint16_t aTimeSeconds,
+                                             uint16_t aServiceClass)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyCFStateChanged(aSuccess, aAction, aReason,
+                                  nsAutoString(aNumber), aTimeSeconds,
+                                  aServiceClass) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyEmergencyCbModeChanged(bool aActive,
+                                                     uint32_t aTimeoutMs)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyEmergencyCbModeChanged(aActive, aTimeoutMs)
+         ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyOtaStatusChanged(const nsAString& aStatus)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyOtaStatusChanged(nsAutoString(aStatus))
+         ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyIccChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsAutoString iccId;
+  mService->GetIccId(mClientId, iccId);
+
+  return SendNotifyIccChanged(iccId) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyRadioStateChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsAutoString radioState;
+  rv = mService->GetRadioState(mClientId, radioState);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SendNotifyRadioStateChanged(radioState) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyClirModeChanged(uint32_t aMode)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return SendNotifyClirModeChanged(aMode) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyLastKnownNetworkChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsAutoString network;
+  rv = mService->GetLastKnownNetwork(mClientId, network);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SendNotifyLastNetworkChanged(network) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyLastKnownHomeNetworkChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsAutoString network;
+  rv = mService->GetLastKnownHomeNetwork(mClientId, network);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SendNotifyLastHomeNetworkChanged(network) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+MobileConnectionParent::NotifyNetworkSelectionModeChanged()
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  nsAutoString mode;
+  rv = mService->GetNetworkSelectionMode(mClientId, mode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SendNotifyNetworkSelectionModeChanged(mode) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/******************************************************************************
+ * PMobileConnectionRequestParent
+ ******************************************************************************/
+
+void
+MobileConnectionRequestParent::ActorDestroy(ActorDestroyReason why)
+{
+  mLive = false;
+  mService = nullptr;
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetNetworksRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetNetworks(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SelectNetworkRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  // Use dont_AddRef here because this instances is already AddRef-ed in
+  // MobileConnectionIPCSerializer.h
+  nsCOMPtr<nsIMobileNetworkInfo> network = dont_AddRef(aRequest.network());
+  return NS_SUCCEEDED(mService->SelectNetwork(mClientId, network, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SelectNetworkAutoRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SelectNetworkAutomatically(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetPreferredNetworkTypeRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetPreferredNetworkType(mClientId, aRequest.type(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetPreferredNetworkTypeRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetPreferredNetworkType(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetRoamingPreferenceRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetRoamingPreference(mClientId, aRequest.mode(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetRoamingPreferenceRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetRoamingPreference(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetVoicePrivacyModeRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetVoicePrivacyMode(mClientId, aRequest.enabled(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetVoicePrivacyModeRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetVoicePrivacyMode(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SendMmiRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SendMMI(mClientId, aRequest.mmi(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const CancelMmiRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->CancelMMI(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetCallForwardingRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  AutoSafeJSContext cx;
+  JS::Rooted<JS::Value> options(cx);
+  if (!ToJSValue(cx, aRequest.options(), &options)) {
+    JS_ClearPendingException(cx);
+    return false;
+  }
+
+  return NS_SUCCEEDED(mService->SetCallForwarding(mClientId, options, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetCallForwardingRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetCallForwarding(mClientId, aRequest.reason(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetCallBarringRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  AutoSafeJSContext cx;
+  JS::Rooted<JS::Value> options(cx);
+  if (!ToJSValue(cx, aRequest.options(), &options)) {
+    JS_ClearPendingException(cx);
+    return false;
+  }
+
+  return NS_SUCCEEDED(mService->SetCallBarring(mClientId, options, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetCallBarringRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  AutoSafeJSContext cx;
+  JS::Rooted<JS::Value> options(cx);
+  if (!ToJSValue(cx, aRequest.options(), &options)) {
+    JS_ClearPendingException(cx);
+    return false;
+  }
+
+  return NS_SUCCEEDED(mService->GetCallBarring(mClientId, options, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const ChangeCallBarringPasswordRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  AutoSafeJSContext cx;
+  JS::Rooted<JS::Value> options(cx);
+  if (!ToJSValue(cx, aRequest.options(), &options)) {
+    JS_ClearPendingException(cx);
+    return false;
+  }
+
+  return NS_SUCCEEDED(mService->ChangeCallBarringPassword(mClientId, options, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetCallWaitingRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetCallWaiting(mClientId, aRequest.enabled(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetCallWaitingRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetCallWaiting(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetCallingLineIdRestrictionRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetCallingLineIdRestriction(mClientId, aRequest.mode(), this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const GetCallingLineIdRestrictionRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->GetCallingLineIdRestriction(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const ExitEmergencyCbModeRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->ExitEmergencyCbMode(mClientId, this));
+}
+
+bool
+MobileConnectionRequestParent::DoRequest(const SetRadioEnabledRequest& aRequest)
+{
+  NS_ENSURE_TRUE(mService, false);
+
+  return NS_SUCCEEDED(mService->SetRadioEnabled(mClientId, aRequest.enabled(), this));
+}
+
+nsresult
+MobileConnectionRequestParent::SendReply(const MobileConnectionReply& aReply)
+{
+  NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
+
+  return Send__delete__(this, aReply) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// nsIMobileConnectionListener
+
+NS_IMPL_ISUPPORTS(MobileConnectionRequestParent, nsIMobileConnectionCallback);
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifySuccess()
+{
+  return SendReply(MobileConnectionReplySuccess());
+}
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifySuccessWithString(const nsAString& aResult)
+{
+  return SendReply(MobileConnectionReplySuccessString(nsAutoString(aResult)));
+}
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifySuccessWithBoolean(bool aResult)
+{
+  return SendReply(MobileConnectionReplySuccessBoolean(aResult));
+}
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifyGetNetworksSuccess(uint32_t aCount,
+                                                        nsIMobileNetworkInfo** aNetworks)
+{
+  nsTArray<nsIMobileNetworkInfo*> networks;
+  for (uint32_t i = 0; i < aCount; i++) {
+    nsCOMPtr<nsIMobileNetworkInfo> network = aNetworks[i];
+    // We release the ref after serializing process is finished in
+    // MobileConnectionIPCSerializer.
+    networks.AppendElement(network.forget().take());
+  }
+  return SendReply(MobileConnectionReplySuccessNetworks(networks));
+}
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifySendCancelMmiSuccess(JS::Handle<JS::Value> aResult)
+{
+  AutoSafeJSContext cx;
+  RootedDictionary<MozMMIResult> result(cx);
+
+  if (!result.Init(cx, aResult)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  // No additionInformation passed
+  if (!result.mAdditionalInformation.WasPassed()) {
+    return SendReply(MobileConnectionReplySuccessMmi(result.mServiceCode,
+                                                     result.mStatusMessage,
+                                                     AdditionalInformation(mozilla::void_t())));
+  }
+
+  OwningUnsignedShortOrObject& additionInformation = result.mAdditionalInformation.Value();
+
+  if (additionInformation.IsUnsignedShort()) {
+    return SendReply(MobileConnectionReplySuccessMmi(result.mServiceCode,
+                                                     result.mStatusMessage,
+                                                     AdditionalInformation(uint16_t(additionInformation.GetAsUnsignedShort()))));
+  }
+
+  if (additionInformation.IsObject()) {
+    uint32_t length;
+    JS::Rooted<JS::Value> value(cx);
+    JS::Rooted<JSObject*> object(cx, additionInformation.GetAsObject());
+
+    if (!JS_IsArrayObject(cx, object) ||
+        !JS_GetArrayLength(cx, object, &length) || length <= 0 ||
+        // Check first element to decide the format of array.
+        !JS_GetElement(cx, object, 0, &value)) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    // Check first element to decide the format of array.
+    if (value.isString()) {
+      // String[]
+      nsTArray<nsString> infos;
+      for (uint32_t i = 0; i < length; i++) {
+        if (!JS_GetElement(cx, object, i, &value) || !value.isString()) {
+          return NS_ERROR_TYPE_ERR;
+        }
+
+        nsAutoJSString str;
+        if (!str.init(cx, value.toString())) {
+          return NS_ERROR_FAILURE;
+        }
+        infos.AppendElement(str);
+      }
+
+      return SendReply(MobileConnectionReplySuccessMmi(result.mServiceCode,
+                                                       result.mStatusMessage,
+                                                       AdditionalInformation(infos)));
+    } else {
+      // IPC::MozCallForwardingOptions[]
+      nsTArray<IPC::MozCallForwardingOptions> infos;
+      for (uint32_t i = 0; i < length; i++) {
+        IPC::MozCallForwardingOptions info;
+        if (!JS_GetElement(cx, object, i, &value) || !info.Init(cx, value)) {
+          return NS_ERROR_TYPE_ERR;
+        }
+
+        infos.AppendElement(info);
+      }
+
+      return SendReply(MobileConnectionReplySuccessMmi(result.mServiceCode,
+                                                       result.mStatusMessage,
+                                                       AdditionalInformation(infos)));
+    }
+  }
+
+  return NS_ERROR_TYPE_ERR;
+}
+
+NS_IMETHODIMP
+MobileConnectionRequestParent::NotifyGetCallForwardingSuccess(JS::Handle<JS::Value> aResults)
+{