Merge b2g-inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 18 Oct 2013 15:00:58 -0400
changeset 166041 a0fc55fdf0ca9d24d8030c59ddabb023f915fea5
parent 166017 43b40be4049e849a86582a7943349c70c8e7abc9 (current diff)
parent 166040 d86d0411faad5172bd3f031829c6774d34058142 (diff)
child 166062 51dee39976fb76a469a558ee8f6cf5559192dfc2
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
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/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1254,33 +1254,72 @@ window.addEventListener('ContentStart', 
       channel: aData
     });
     shell.visibleNormalAudioActive = (aData == 'normal');
 }, "visible-audio-channel-changed", false);
 })();
 
 (function recordingStatusTracker() {
   let gRecordingActiveCount = 0;
+  let gRecordingActiveProcesses = {};
 
-  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+  let recordingHandler = function(aSubject, aTopic, aData) {
     let oldCount = gRecordingActiveCount;
-    if (aData == "starting") {
-      gRecordingActiveCount += 1;
-    } else if (aData == "shutdown") {
-      gRecordingActiveCount -= 1;
+
+    let processId = (!aSubject) ? 'main'
+                                : aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
+    if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) {
+      gRecordingActiveProcesses[processId] = 0;
     }
 
-    // We need to track changes from 1 <-> 0
-    if (gRecordingActiveCount + oldCount == 1) {
+    let currentActive = gRecordingActiveProcesses[processId];
+    switch (aData) {
+      case 'starting':
+        gRecordingActiveCount++;
+        currentActive++;
+        break;
+      case 'shutdown':
+        // Bug 928206 will make shutdown be sent even if no starting.
+        if (currentActive > 0) {
+          gRecordingActiveCount--;
+          currentActive--;
+        }
+        break;
+      case 'content-shutdown':
+        gRecordingActiveCount -= currentActive;
+        currentActive = 0;
+        break;
+    }
+
+    if (currentActive > 0) {
+      gRecordingActiveProcesses[processId] = currentActive;
+    } else {
+      delete gRecordingActiveProcesses[processId];
+    }
+
+    // We need to track changes from N <-> 0
+    if ((oldCount === 0 && gRecordingActiveCount > 0) ||
+        (gRecordingActiveCount === 0 && oldCount > 0)) {
       shell.sendChromeEvent({
         type: 'recording-status',
-        active: (gRecordingActiveCount == 1)
+        active: (gRecordingActiveCount > 0)
       });
     }
-}, "recording-device-events", false);
+  };
+  Services.obs.addObserver(recordingHandler, 'recording-device-events', false);
+  Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false);
+
+  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+    // send additional recording events if content process is being killed
+    let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
+    let childId = aSubject.get('childID');
+    if (gRecordingActiveProcesses.hasOwnProperty(childId) >= 0) {
+      Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown');
+    }
+  }, 'ipc:content-shutdown', false);
 })();
 
 (function volumeStateTracker() {
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
       type: 'volume-state-changed',
       active: (aData == 'Shared')
     });
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "154bb18c48ff06e41fb7ba24d8f72d520919646f", 
+    "revision": "723401621af1bfabb671ec53ac7cafd62b700cb9", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -556,17 +556,18 @@ BluetoothHfpManager::HandleVolumeChanged
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged()
 {
   nsCOMPtr<nsIMobileConnectionProvider> connection =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
-  connection->GetVoiceConnectionInfo(getter_AddRefs(voiceInfo));
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
+  connection->GetVoiceConnectionInfo(0, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   bool roaming;
   voiceInfo->GetRoaming(&roaming);
@@ -592,17 +593,18 @@ BluetoothHfpManager::HandleVoiceConnecti
 
   /**
    * Possible return values for mode are:
    * - null (unknown): set mNetworkSelectionMode to 0 (auto)
    * - automatic: set mNetworkSelectionMode to 0 (auto)
    * - manual: set mNetworkSelectionMode to 1 (manual)
    */
   nsString mode;
-  connection->GetNetworkSelectionMode(mode);
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
+  connection->GetNetworkSelectionMode(0, mode);
   if (mode.EqualsLiteral("manual")) {
     mNetworkSelectionMode = 1;
   } else {
     mNetworkSelectionMode = 0;
   }
 
   nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -287,30 +287,32 @@ BluetoothRilListener::StopIccListening()
 
 bool
 BluetoothRilListener::StartMobileConnectionListening()
 {
   nsCOMPtr<nsIMobileConnectionProvider> provider =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
   nsresult rv = provider->
-                  RegisterMobileConnectionMsg(mMobileConnectionListener);
+                  RegisterMobileConnectionMsg(0, mMobileConnectionListener);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 BluetoothRilListener::StopMobileConnectionListening()
 {
   nsCOMPtr<nsIMobileConnectionProvider> provider =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
   nsresult rv = provider->
-                  UnregisterMobileConnectionMsg(mMobileConnectionListener);
+                  UnregisterMobileConnectionMsg(0, mMobileConnectionListener);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 BluetoothRilListener::StartTelephonyListening()
 {
   nsCOMPtr<nsITelephonyProvider> provider =
     do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1935,17 +1935,22 @@ ContentParent::RecvBroadcastVolume(const
 #endif
 }
 
 bool
 ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus)
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
-        obs->NotifyObservers(nullptr, "recording-device-events", aRecordingStatus.get());
+        // recording-device-ipc-events needs to gather more information from content process
+        nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+        props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
+        obs->NotifyObservers((nsIPropertyBag2*) props,
+                             "recording-device-ipc-events",
+                             aRecordingStatus.get());
     } else {
         NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
     }
     return true;
 }
 
 bool
 ContentParent::SendNuwaFork()
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -51,16 +51,17 @@ this.SystemMessagePermissionsTable = {
   },
   "bluetooth-opp-receiving-file-confirmation": {
     "bluetooth": []
   },
   "bluetooth-opp-transfer-start": {
     "bluetooth": []
   },
   "connection": { },
+  "dummy-system-message": { }, // for system message testing framework
   "headset-button": { },
   "icc-stkcommand": {
     "settings": ["read", "write"]
   },
   "media-button": { },
   "notification": {
     "desktop-notification": []
   },
--- a/dom/network/interfaces/nsIMobileConnectionProvider.idl
+++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl
@@ -28,60 +28,84 @@ interface nsIMobileConnectionListener : 
                                     in unsigned long timeoutMs);
   void notifyOtaStatusChanged(in DOMString status);
 };
 
 /**
  * XPCOM component (in the content process) that provides the mobile
  * network information.
  */
-[scriptable, uuid(c66652e0-0628-11e3-8ffd-0800200c9a66)]
+[scriptable, uuid(84278a49-0f05-4585-b3f4-c74882ae5719)]
 interface nsIMobileConnectionProvider : nsISupports
 {
   /**
    * 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 nsIMobileConnectionListener listener);
-  void unregisterMobileConnectionMsg(in nsIMobileConnectionListener listener);
+  void registerMobileConnectionMsg(in unsigned long clientId,
+                                   in nsIMobileConnectionListener listener);
+  void unregisterMobileConnectionMsg(in unsigned long clientId,
+                                     in nsIMobileConnectionListener listener);
+
+  nsIDOMMozMobileConnectionInfo getVoiceConnectionInfo(in unsigned long clientId);
+  nsIDOMMozMobileConnectionInfo getDataConnectionInfo(in unsigned long clientId);
+  DOMString getIccId(in unsigned long clientId);
+  DOMString getNetworkSelectionMode(in unsigned long clientId);
 
-  readonly attribute nsIDOMMozMobileConnectionInfo voiceConnectionInfo;
-  readonly attribute nsIDOMMozMobileConnectionInfo dataConnectionInfo;
-  readonly attribute DOMString networkSelectionMode;
+  nsIDOMDOMRequest getNetworks(in unsigned long clientId,
+                               in nsIDOMWindow window);
+  nsIDOMDOMRequest selectNetwork(in unsigned long clientId,
+                                 in nsIDOMWindow window,
+                                 in nsIDOMMozMobileNetworkInfo network);
+  nsIDOMDOMRequest selectNetworkAutomatically(in unsigned long clientId,
+                                              in nsIDOMWindow window);
 
-  nsIDOMDOMRequest getNetworks(in nsIDOMWindow window);
-  nsIDOMDOMRequest selectNetwork(in nsIDOMWindow window, in nsIDOMMozMobileNetworkInfo network);
-  nsIDOMDOMRequest selectNetworkAutomatically(in nsIDOMWindow window);
-
-  nsIDOMDOMRequest setRoamingPreference(in nsIDOMWindow window,
+  nsIDOMDOMRequest setRoamingPreference(in unsigned long clientId,
+                                        in nsIDOMWindow window,
                                         in DOMString mode);
-  nsIDOMDOMRequest getRoamingPreference(in nsIDOMWindow window);
+  nsIDOMDOMRequest getRoamingPreference(in unsigned long clientId,
+                                        in nsIDOMWindow window);
 
-  nsIDOMDOMRequest setVoicePrivacyMode(in nsIDOMWindow window,
+  nsIDOMDOMRequest setVoicePrivacyMode(in unsigned long clientId,
+                                       in nsIDOMWindow window,
                                        in bool enabled);
-  nsIDOMDOMRequest getVoicePrivacyMode(in nsIDOMWindow window);
+  nsIDOMDOMRequest getVoicePrivacyMode(in unsigned long clientId,
+                                       in nsIDOMWindow window);
 
-  nsIDOMDOMRequest sendMMI(in nsIDOMWindow window, in DOMString mmi);
-  nsIDOMDOMRequest cancelMMI(in nsIDOMWindow window);
+  nsIDOMDOMRequest sendMMI(in unsigned long clientId,
+                           in nsIDOMWindow window,
+                           in DOMString mmi);
+  nsIDOMDOMRequest cancelMMI(in unsigned long clientId,
+                             in nsIDOMWindow window);
 
-  nsIDOMDOMRequest getCallForwardingOption(in nsIDOMWindow   window,
+  nsIDOMDOMRequest getCallForwardingOption(in unsigned long clientId,
+                                           in nsIDOMWindow window,
                                            in unsigned short reason);
-  nsIDOMDOMRequest setCallForwardingOption(in nsIDOMWindow          window,
+  nsIDOMDOMRequest setCallForwardingOption(in unsigned long clientId,
+                                           in nsIDOMWindow window,
                                            in nsIDOMMozMobileCFInfo CFInfo);
 
-  nsIDOMDOMRequest getCallBarringOption(in nsIDOMWindow window,
-                                        in jsval        option);
-  nsIDOMDOMRequest setCallBarringOption(in nsIDOMWindow window,
-                                        in jsval        option);
-  nsIDOMDOMRequest changeCallBarringPassword(in nsIDOMWindow window,
+  nsIDOMDOMRequest getCallBarringOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
+                                        in jsval option);
+  nsIDOMDOMRequest setCallBarringOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
+                                        in jsval option);
+  nsIDOMDOMRequest changeCallBarringPassword(in unsigned long clientId,
+                                             in nsIDOMWindow window,
                                              in jsval info);
 
-  nsIDOMDOMRequest setCallWaitingOption(in nsIDOMWindow   window,
+  nsIDOMDOMRequest setCallWaitingOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
                                         in bool enabled);
-  nsIDOMDOMRequest getCallWaitingOption(in nsIDOMWindow   window);
+  nsIDOMDOMRequest getCallWaitingOption(in unsigned long clientId,
+                                        in nsIDOMWindow window);
 
-  nsIDOMDOMRequest setCallingLineIdRestriction(in nsIDOMWindow   window,
+  nsIDOMDOMRequest setCallingLineIdRestriction(in unsigned long clientId,
+                                               in nsIDOMWindow window,
                                                in unsigned short clirMode);
-  nsIDOMDOMRequest getCallingLineIdRestriction(in nsIDOMWindow   window);
+  nsIDOMDOMRequest getCallingLineIdRestriction(in unsigned long clientId,
+                                               in nsIDOMWindow window);
 
-  nsIDOMDOMRequest exitEmergencyCbMode(in nsIDOMWindow window);
+  nsIDOMDOMRequest exitEmergencyCbMode(in unsigned long clientId,
+                                       in nsIDOMWindow window);
 };
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -78,16 +78,19 @@ NS_IMPL_EVENT_HANDLER(MobileConnection, 
 NS_IMPL_EVENT_HANDLER(MobileConnection, emergencycbmodechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, otastatuschange)
 
 MobileConnection::MobileConnection()
 {
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   mWindow = nullptr;
 
+  // TODO: Bug 814629 - WebMobileConnection API: support multiple sim cards
+  mClientId = 0;
+
   // Not being able to acquire the provider isn't fatal since we check
   // for it explicitly below.
   if (!mProvider) {
     NS_WARNING("Could not acquire nsIMobileConnectionProvider!");
     return;
   }
 }
 
@@ -96,30 +99,30 @@ MobileConnection::Init(nsPIDOMWindow* aW
 {
   BindToOwner(aWindow);
 
   mWindow = do_GetWeakReference(aWindow);
   mListener = new Listener(this);
 
   if (!CheckPermission("mobilenetwork") &&
       CheckPermission("mobileconnection")) {
-    DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mListener);
+    DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mClientId, mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Failed registering mobile connection messages with provider");
 
     printf_stderr("MobileConnection initialized");
   }
 }
 
 void
 MobileConnection::Shutdown()
 {
   if (mProvider && mListener) {
     mListener->Disconnect();
-    mProvider->UnregisterMobileConnectionMsg(mListener);
+    mProvider->UnregisterMobileConnectionMsg(mClientId, mListener);
     mProvider = nullptr;
     mListener = nullptr;
   }
 }
 
 // nsIDOMMozMobileConnection
 
 NS_IMETHODIMP
@@ -168,347 +171,347 @@ MobileConnection::CheckPermission(const 
 NS_IMETHODIMP
 MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
 {
   *voice = nullptr;
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
-  return mProvider->GetVoiceConnectionInfo(voice);
+  return mProvider->GetVoiceConnectionInfo(mClientId, voice);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetData(nsIDOMMozMobileConnectionInfo** data)
 {
   *data = nullptr;
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
-  return mProvider->GetDataConnectionInfo(data);
+  return mProvider->GetDataConnectionInfo(mClientId, data);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetNetworkSelectionMode(nsAString& networkSelectionMode)
 {
   networkSelectionMode.SetIsVoid(true);
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
      return NS_OK;
   }
-  return mProvider->GetNetworkSelectionMode(networkSelectionMode);
+  return mProvider->GetNetworkSelectionMode(mClientId, networkSelectionMode);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetNetworks(nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetNetworks(GetOwner(), request);
+  return mProvider->GetNetworks(mClientId, GetOwner(), request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SelectNetwork(nsIDOMMozMobileNetworkInfo* network, nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SelectNetwork(GetOwner(), network, request);
+  return mProvider->SelectNetwork(mClientId, GetOwner(), network, request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SelectNetworkAutomatically(nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SelectNetworkAutomatically(GetOwner(), request);
+  return mProvider->SelectNetworkAutomatically(mClientId, GetOwner(), request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetRoamingPreference(const nsAString& aMode, nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetRoamingPreference(GetOwner(), aMode, aDomRequest);
+  return mProvider->SetRoamingPreference(mClientId, GetOwner(), aMode, aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetRoamingPreference(nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetRoamingPreference(GetOwner(), aDomRequest);
+  return mProvider->GetRoamingPreference(mClientId, GetOwner(), aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetVoicePrivacyMode(bool aEnabled, nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetVoicePrivacyMode(GetOwner(), aEnabled, aDomRequest);
+  return mProvider->SetVoicePrivacyMode(mClientId, GetOwner(), aEnabled, aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetVoicePrivacyMode(nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetVoicePrivacyMode(GetOwner(), aDomRequest);
+  return mProvider->GetVoicePrivacyMode(mClientId, GetOwner(), aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SendMMI(const nsAString& aMMIString,
                           nsIDOMDOMRequest** aRequest)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SendMMI(GetOwner(), aMMIString, aRequest);
+  return mProvider->SendMMI(mClientId, GetOwner(), aMMIString, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::CancelMMI(nsIDOMDOMRequest** aRequest)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->CancelMMI(GetOwner(), aRequest);
+  return mProvider->CancelMMI(mClientId, GetOwner(),aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallForwardingOption(uint16_t aReason,
                                           nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallForwardingOption(GetOwner(), aReason, aRequest);
+  return mProvider->GetCallForwardingOption(mClientId, GetOwner(), aReason, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallForwardingOption(nsIDOMMozMobileCFInfo* aCFInfo,
                                           nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallForwardingOption(GetOwner(), aCFInfo, aRequest);
+  return mProvider->SetCallForwardingOption(mClientId, GetOwner(), aCFInfo, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallBarringOption(const JS::Value& aOption,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallBarringOption(GetOwner(), aOption, aRequest);
+  return mProvider->GetCallBarringOption(mClientId, GetOwner(), aOption, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallBarringOption(const JS::Value& aOption,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallBarringOption(GetOwner(), aOption, aRequest);
+  return mProvider->SetCallBarringOption(mClientId, GetOwner(), aOption, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::ChangeCallBarringPassword(const JS::Value& aInfo,
                                             nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->ChangeCallBarringPassword(GetOwner(), aInfo, aRequest);
+  return mProvider->ChangeCallBarringPassword(mClientId, GetOwner(), aInfo, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallWaitingOption(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallWaitingOption(GetOwner(), aRequest);
+  return mProvider->GetCallWaitingOption(mClientId, GetOwner(), aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallWaitingOption(bool aEnabled,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallWaitingOption(GetOwner(), aEnabled, aRequest);
+  return mProvider->SetCallWaitingOption(mClientId, GetOwner(), aEnabled, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallingLineIdRestriction(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallingLineIdRestriction(GetOwner(), aRequest);
+  return mProvider->GetCallingLineIdRestriction(mClientId, GetOwner(), aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallingLineIdRestriction(unsigned short aClirMode,
                                               nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallingLineIdRestriction(GetOwner(), aClirMode, aRequest);
+  return mProvider->SetCallingLineIdRestriction(mClientId, GetOwner(), aClirMode, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::ExitEmergencyCbMode(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->ExitEmergencyCbMode(GetOwner(), aRequest);
+  return mProvider->ExitEmergencyCbMode(mClientId, GetOwner(), aRequest);
 }
 
 // nsIMobileConnectionListener
 
 NS_IMETHODIMP
 MobileConnection::NotifyVoiceChanged()
 {
   if (!CheckPermission("mobileconnection")) {
--- a/dom/network/src/MobileConnection.h
+++ b/dom/network/src/MobileConnection.h
@@ -43,16 +43,18 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MobileConnection,
                                            nsDOMEventTargetHelper)
 
 private:
   nsCOMPtr<nsIMobileConnectionProvider> mProvider;
   nsRefPtr<Listener> mListener;
   nsWeakPtr mWindow;
 
+  uint32_t mClientId;
+
   bool CheckPermission(const char* type);
 };
 
 } // namespace network
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_network_MobileConnection_h
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -39,17 +39,18 @@ this.PhoneNumberUtils = {
   _mcc: '724',
 
   getCountryName: function getCountryName() {
     let mcc;
     let countryName;
 
 #ifdef MOZ_B2G_RIL
     // Get network mcc
-    let voice = mobileConnection.voiceConnectionInfo;
+    // TODO: Bug 926740 - PhoneNumberUtils for multisim
+    let voice = mobileConnection.getVoiceConnectionInfo(0);
     if (voice && voice.network && voice.network.mcc) {
       mcc = voice.network.mcc;
     }
 
     // Get SIM mcc
     let iccInfo = icc.iccInfo;
     if (!mcc && iccInfo && iccInfo.mcc) {
       mcc = iccInfo.mcc;
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -83,23 +83,23 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:CardLockResult",
   "RIL:CardLockRetryCount",
   "RIL:USSDReceived",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:StkCommand",
   "RIL:StkSessionEnd",
   "RIL:DataError",
-  "RIL:SetCallForwardingOption",
-  "RIL:GetCallForwardingOption",
-  "RIL:SetCallBarringOption",
-  "RIL:GetCallBarringOption",
+  "RIL:SetCallForwardingOptions",
+  "RIL:GetCallForwardingOptions",
+  "RIL:SetCallBarringOptions",
+  "RIL:GetCallBarringOptions",
   "RIL:ChangeCallBarringPassword",
-  "RIL:SetCallWaitingOption",
-  "RIL:GetCallWaitingOption",
+  "RIL:SetCallWaitingOptions",
+  "RIL:GetCallWaitingOptions",
   "RIL:SetCallingLineIdRestriction",
   "RIL:GetCallingLineIdRestriction",
   "RIL:CellBroadcastReceived",
   "RIL:CfStateChanged",
   "RIL:IccOpenChannel",
   "RIL:IccCloseChannel",
   "RIL:IccExchangeAPDU",
   "RIL:ReadIccContacts",
@@ -379,23 +379,23 @@ CellBroadcastEtwsInfo.prototype = {
 
   // nsIDOMMozCellBroadcastEtwsInfo
 
   warningType: null,
   emergencyUserAlert: null,
   popup: null
 };
 
-function CallBarringOption(option) {
-  this.program = option.program;
-  this.enabled = option.enabled;
-  this.password = option.password;
-  this.serviceClass = option.serviceClass;
+function CallBarringOptions(options) {
+  this.program = options.program;
+  this.enabled = options.enabled;
+  this.password = options.password;
+  this.serviceClass = options.serviceClass;
 }
-CallBarringOption.prototype = {
+CallBarringOptions.prototype = {
   __exposedProps__ : {program: 'r',
                       enabled: 'r',
                       password: 'r',
                       serviceClass: 'r'}
 };
 
 function DOMMMIResult(result) {
   this.serviceCode = result.serviceCode;
@@ -444,27 +444,40 @@ IccCardLockError.prototype = {
   __init: function(lockType, errorMsg, retryCount) {
     this.__DOM_IMPL__.init(errorMsg);
     this.lockType = lockType;
     this.retryCount = retryCount;
   },
 };
 
 function RILContentHelper() {
-  this.rilContext = {
-    cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
-    networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
-    iccInfo:              null,
-    voiceConnectionInfo:  new MobileConnectionInfo(),
-    dataConnectionInfo:   new MobileConnectionInfo()
-  };
+
+  this.numClients = gNumRadioInterfaces;
+  debug("Number of clients: " + this.numClients);
+
+  this.rilContexts = [];
+  for (let clientId = 0; clientId < this.numClients; clientId++) {
+    this.rilContexts[clientId] = {
+      cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
+      networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
+      iccInfo:              null,
+      voiceConnectionInfo:  new MobileConnectionInfo(),
+      dataConnectionInfo:   new MobileConnectionInfo()
+    };
+  }
   this.voicemailInfo = new VoicemailInfo();
 
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
+  this._selectingNetworks = [];
+  this._mobileConnectionListeners = [];
+  this._cellBroadcastListeners = [];
+  this._voicemailListeners = [];
+  this._iccListeners = [];
+
   Services.obs.addObserver(this, "xpcom-shutdown", false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider,
                                          Ci.nsICellBroadcastProvider,
@@ -520,123 +533,138 @@ RILContentHelper.prototype = {
     this.updateInfo(srcNetwork, network);
   },
 
   /**
    * We need to consider below cases when update iccInfo:
    * 1. Should clear iccInfo to null if there is no card detected.
    * 2. Need to create corresponding object based on iccType.
    */
-  updateIccInfo: function updateIccInfo(newInfo) {
+  updateIccInfo: function updateIccInfo(clientId, newInfo) {
+    let rilContext = this.rilContexts[clientId];
+
     // Card is not detected, clear iccInfo to null.
     if (!newInfo || !newInfo.iccType) {
-      this.rilContext.iccInfo = null;
+      rilContext.iccInfo = null;
       return;
     }
 
     // If iccInfo is null, new corresponding object based on iccType.
-    if (!this.rilContext.iccInfo) {
+    if (!rilContext.iccInfo) {
       if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
-        this.rilContext.iccInfo = new CdmaIccInfo();
+        rilContext.iccInfo = new CdmaIccInfo();
       } else {
-        this.rilContext.iccInfo = new GsmIccInfo();
+        rilContext.iccInfo = new GsmIccInfo();
       }
     }
 
-    this.updateInfo(newInfo, this.rilContext.iccInfo);
+    this.updateInfo(newInfo, rilContext.iccInfo);
   },
 
   _windowsMap: null,
 
-  rilContext: null,
+  rilContexts: null,
 
-  getRilContext: function getRilContext() {
-    // Update ril context by sending IPC message to chrome only when the first
+  getRilContext: function getRilContext(clientId) {
+    // Update ril contexts by sending IPC message to chrome only when the first
     // time we require it. The information will be updated by following info
     // changed messages.
-    this.getRilContext = function getRilContext() {
-      return this.rilContext;
+    this.getRilContext = function getRilContext(clientId) {
+      return this.rilContexts[clientId];
     };
 
-    let rilContext =
-      cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: 0})[0];
-    if (!rilContext) {
-      debug("Received null rilContext from chrome process.");
-      return;
+    for (let cId = 0; cId < this.numClients; cId++) {
+      let rilContext =
+        cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: cId})[0];
+      if (!rilContext) {
+        debug("Received null rilContext from chrome process.");
+        continue;
+      }
+      this.rilContexts[cId].cardState = rilContext.cardState;
+      this.rilContexts[cId].networkSelectionMode = rilContext.networkSelectionMode;
+      this.updateIccInfo(cId, rilContext.iccInfo);
+      this.updateConnectionInfo(rilContext.voice, this.rilContexts[cId].voiceConnectionInfo);
+      this.updateConnectionInfo(rilContext.data, this.rilContexts[cId].dataConnectionInfo);
     }
-    this.rilContext.cardState = rilContext.cardState;
-    this.rilContext.networkSelectionMode = rilContext.networkSelectionMode;
-    this.updateIccInfo(rilContext.iccInfo);
-    this.updateConnectionInfo(rilContext.voice, this.rilContext.voiceConnectionInfo);
-    this.updateConnectionInfo(rilContext.data, this.rilContext.dataConnectionInfo);
+
+    return this.rilContexts[clientId];
+  },
+
+  /**
+   * nsIIccProvider
+   */
 
-    return this.rilContext;
+  get iccInfo() {
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    let context = this.getRilContext(0);
+    return context && context.iccInfo;
+  },
+
+  get cardState() {
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    let context = this.getRilContext(0);
+    return context && context.cardState;
   },
 
   /**
    * nsIMobileConnectionProvider
    */
 
-  get iccInfo() {
-    let context = this.getRilContext();
-    return context && context.iccInfo;
-  },
-
-  get voiceConnectionInfo() {
-    let context = this.getRilContext();
+  getVoiceConnectionInfo: function getVoiceConnectionInfo(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.voiceConnectionInfo;
   },
 
-  get dataConnectionInfo() {
-    let context = this.getRilContext();
+  getDataConnectionInfo: function getDataConnectionInfo(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.dataConnectionInfo;
   },
 
-  get cardState() {
-    let context = this.getRilContext();
-    return context && context.cardState;
+  getIccId: function getIccId(clientId) {
+    let context = this.getRilContext(clientId);
+    return context && context.iccInfo.iccid;
   },
 
-  get networkSelectionMode() {
-    let context = this.getRilContext();
+  getNetworkSelectionMode: function getNetworkSelectionMode(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.networkSelectionMode;
   },
 
   /**
-   * The network that is currently trying to be selected (or "automatic").
-   * This helps ensure that only one network is selected at a time.
+   * 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,
+  _selectingNetworks: null,
 
-  getNetworks: function getNetworks(window) {
+  getNetworks: function getNetworks(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetAvailableNetworks", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  selectNetwork: function selectNetwork(window, network) {
+  selectNetwork: function selectNetwork(clientId, window, network) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
-    if (this._selectingNetwork) {
-      throw new Error("Already selecting a network: " + this._selectingNetwork);
+    if (this._selectingNetworks[clientId]) {
+      throw new Error("Already selecting a network: " + this._selectingNetworks[clientId]);
     }
 
     if (!network) {
       throw new Error("Invalid network provided: " + network);
     }
 
     if (isNaN(parseInt(network.mnc, 10))) {
       throw new Error("Invalid network MNC: " + network.mnc);
@@ -644,143 +672,143 @@ RILContentHelper.prototype = {
 
     if (isNaN(parseInt(network.mcc, 10))) {
       throw new Error("Invalid network MCC: " + network.mcc);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
-        this.rilContext.voiceConnectionInfo.network === network) {
+    if (this.rilContexts[clientId].networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
+        this.rilContexts[clientId].voiceConnectionInfo.network === network) {
 
       // Already manually selected this network, so schedule
       // onsuccess to be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
-    this._selectingNetwork = network;
+    this._selectingNetworks[clientId] = network;
 
     cpmm.sendAsyncMessage("RIL:SelectNetwork", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         mnc: network.mnc,
         mcc: network.mcc
       }
     });
 
     return request;
   },
 
-  selectNetworkAutomatically: function selectNetworkAutomatically(window) {
+  selectNetworkAutomatically: function selectNetworkAutomatically(clientId, window) {
 
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
-    if (this._selectingNetwork) {
-      throw new Error("Already selecting a network: " + this._selectingNetwork);
+    if (this._selectingNetworks[clientId]) {
+      throw new Error("Already selecting a network: " + this._selectingNetworks[clientId]);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
+    if (this.rilContexts[clientId].networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
       // Already using automatic selection mode, so schedule
       // onsuccess to be be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
-    this._selectingNetwork = "automatic";
+    this._selectingNetworks[clientId] = "automatic";
     cpmm.sendAsyncMessage("RIL:SelectNetworkAuto", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  setRoamingPreference: function setRoamingPreference(window, mode) {
+  setRoamingPreference: function setRoamingPreference(clientId, window, mode) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!mode) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
     cpmm.sendAsyncMessage("RIL:SetRoamingPreference", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         mode: mode
       }
     });
     return request;
   },
 
-  getRoamingPreference: function getRoamingPreference(window) {
+  getRoamingPreference: function getRoamingPreference(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetRoamingPreference", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  setVoicePrivacyMode: function setVoicePrivacyMode(window, enabled) {
+  setVoicePrivacyMode: function setVoicePrivacyMode(clientId, window, enabled) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:SetVoicePrivacyMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         enabled: enabled
       }
     });
     return request;
   },
 
-  getVoicePrivacyMode: function getVoicePrivacyMode(window) {
+  getVoicePrivacyMode: function getVoicePrivacyMode(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetVoicePrivacyMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
   getCardLockState: function getCardLockState(window, lockType) {
@@ -846,48 +874,48 @@ RILContentHelper.prototype = {
       data: {
         lockType: lockType,
         requestId: requestId
       }
     });
     return request;
   },
 
-  sendMMI: function sendMMI(window, mmi) {
+  sendMMI: function sendMMI(clientId, window, mmi) {
     debug("Sending MMI " + mmi);
     if (!window) {
       throw Components.Exception("Can't get window object",
                                  Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
     // We need to save the global window to get the proper MMIError
     // constructor once we get the reply from the parent process.
     this._windowsMap[requestId] = window;
 
     cpmm.sendAsyncMessage("RIL:SendMMI", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         mmi: mmi,
         requestId: requestId
       }
     });
     return request;
   },
 
-  cancelMMI: function cancelMMI(window) {
+  cancelMMI: function cancelMMI(clientId, window) {
     debug("Cancel MMI");
     if (!window) {
       throw Components.Exception("Can't get window object",
                                  Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
     cpmm.sendAsyncMessage("RIL:CancelMMI", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
   sendStkResponse: function sendStkResponse(window, command, response) {
@@ -1071,128 +1099,128 @@ RILContentHelper.prototype = {
         contact: iccContact,
         pin2: pin2
       }
     });
 
     return request;
   },
 
-  getCallForwardingOption: function getCallForwardingOption(window, reason) {
+  getCallForwardingOption: function getCallForwardingOption(clientId, window, reason) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!this._isValidCFReason(reason)){
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:GetCallForwardingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallForwardingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         reason: reason
       }
     });
 
     return request;
   },
 
-  setCallForwardingOption: function setCallForwardingOption(window, cfInfo) {
+  setCallForwardingOption: function setCallForwardingOption(clientId, window, cfInfo) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!cfInfo ||
         !this._isValidCFReason(cfInfo.reason) ||
         !this._isValidCFAction(cfInfo.action)){
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:SetCallForwardingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallForwardingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         active: cfInfo.active,
         action: cfInfo.action,
         reason: cfInfo.reason,
         number: cfInfo.number,
         timeSeconds: cfInfo.timeSeconds
       }
     });
 
     return request;
   },
 
-  getCallBarringOption: function getCallBarringOption(window, option) {
+  getCallBarringOption: function getCallBarringOption(clientId, window, option) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (DEBUG) debug("getCallBarringOption: " + JSON.stringify(option));
-    if (!this._isValidCallBarringOption(option)) {
+    if (!this._isValidCallBarringOptions(option)) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:GetCallBarringOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallBarringOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         program: option.program,
         password: option.password,
         serviceClass: option.serviceClass
       }
     });
     return request;
   },
 
-  setCallBarringOption: function setCallBarringOption(window, option) {
+  setCallBarringOption: function setCallBarringOption(clientId, window, option) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (DEBUG) debug("setCallBarringOption: " + JSON.stringify(option));
-    if (!this._isValidCallBarringOption(option, true)) {
+    if (!this._isValidCallBarringOptions(option, true)) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:SetCallBarringOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallBarringOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         program: option.program,
         enabled: option.enabled,
         password: option.password,
         serviceClass: option.serviceClass
       }
     });
     return request;
   },
 
-  changeCallBarringPassword: function changeCallBarringPassword(window, info) {
+  changeCallBarringPassword: function changeCallBarringPassword(clientId, window, info) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     // Checking valid PIN for supplementary services. See TS.22.004 clause 5.2.
@@ -1200,109 +1228,109 @@ RILContentHelper.prototype = {
         info.newPin == null || !info.newPin.match(/^\d{4}$/)) {
       this.dispatchFireRequestError(requestId, "InvalidPassword");
       return request;
     }
 
     if (DEBUG) debug("changeCallBarringPassword: " + JSON.stringify(info));
     info.requestId = requestId;
     cpmm.sendAsyncMessage("RIL:ChangeCallBarringPassword", {
-      clientId: 0,
+      clientId: clientId,
       data: info
     });
 
     return request;
   },
 
-  getCallWaitingOption: function getCallWaitingOption(window) {
+  getCallWaitingOption: function getCallWaitingOption(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    cpmm.sendAsyncMessage("RIL:GetCallWaitingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallWaitingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
 
     return request;
   },
 
-  setCallWaitingOption: function setCallWaitingOption(window, enabled) {
+  setCallWaitingOption: function setCallWaitingOption(clientId, window, enabled) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    cpmm.sendAsyncMessage("RIL:SetCallWaitingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallWaitingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         enabled: enabled
       }
     });
 
     return request;
   },
 
-  getCallingLineIdRestriction: function getCallingLineIdRestriction(window) {
+  getCallingLineIdRestriction: function getCallingLineIdRestriction(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetCallingLineIdRestriction", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
 
     return request;
   },
 
   setCallingLineIdRestriction:
-    function setCallingLineIdRestriction(window, clirMode) {
+    function setCallingLineIdRestriction(clientId, window, clirMode) {
 
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:SetCallingLineIdRestriction", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         clirMode: clirMode
       }
     });
 
     return request;
   },
 
-  exitEmergencyCbMode: function exitEmergencyCbMode(window) {
+  exitEmergencyCbMode: function exitEmergencyCbMode(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:ExitEmergencyCbMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
       }
     });
 
     return request;
   },
 
@@ -1329,81 +1357,93 @@ RILContentHelper.prototype = {
   },
   get voicemailNumber() {
     return this.getVoicemailInfo().number;
   },
   get voicemailDisplayName() {
     return this.getVoicemailInfo().displayName;
   },
 
-  registerListener: function registerListener(listenerType, listener) {
-    let listeners = this[listenerType];
+  registerListener: function registerListener(listenerType, clientId, listener) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let listeners = this[listenerType][clientId];
     if (!listeners) {
-      listeners = this[listenerType] = [];
+      listeners = this[listenerType][clientId] = [];
     }
 
     if (listeners.indexOf(listener) != -1) {
       throw new Error("Already registered this listener!");
     }
 
     listeners.push(listener);
     if (DEBUG) debug("Registered " + listenerType + " listener: " + listener);
   },
 
-  unregisterListener: function unregisterListener(listenerType, listener) {
-    let listeners = this[listenerType];
+  unregisterListener: function unregisterListener(listenerType, clientId, listener) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let listeners = this[listenerType][clientId];
     if (!listeners) {
       return;
     }
 
     let index = listeners.indexOf(listener);
     if (index != -1) {
       listeners.splice(index, 1);
       if (DEBUG) debug("Unregistered listener: " + listener);
     }
   },
 
-  registerMobileConnectionMsg: function registerMobileConnectionMsg(listener) {
+  registerMobileConnectionMsg: function registerMobileConnectionMsg(clientId, listener) {
     debug("Registering for mobile connection related messages");
-    this.registerListener("_mobileConnectionListeners", listener);
+    this.registerListener("_mobileConnectionListeners", clientId, listener);
     cpmm.sendAsyncMessage("RIL:RegisterMobileConnectionMsg");
   },
 
-  unregisterMobileConnectionMsg: function unregisteMobileConnectionMsg(listener) {
-    this.unregisterListener("_mobileConnectionListeners", listener);
+  unregisterMobileConnectionMsg: function unregisteMobileConnectionMsg(clientId, listener) {
+    this.unregisterListener("_mobileConnectionListeners", clientId, listener);
   },
 
   registerVoicemailMsg: function registerVoicemailMsg(listener) {
     debug("Registering for voicemail-related messages");
-    this.registerListener("_voicemailListeners", listener);
+    //TODO: Bug 814634 - WebVoicemail API: support multiple sim cards.
+    this.registerListener("_voicemailListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterVoicemailMsg");
   },
 
   unregisterVoicemailMsg: function unregisteVoicemailMsg(listener) {
-    this.unregisterListener("_voicemailListeners", listener);
+    //TODO: Bug 814634 - WebVoicemail API: support multiple sim cards.
+    this.unregisterListener("_voicemailListeners", 0, listener);
   },
 
   registerCellBroadcastMsg: function registerCellBroadcastMsg(listener) {
     debug("Registering for Cell Broadcast related messages");
-    this.registerListener("_cellBroadcastListeners", listener);
+    //TODO: Bug 921326 - Cellbroadcast API: support multiple sim cards
+    this.registerListener("_cellBroadcastListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterCellBroadcastMsg");
   },
 
   unregisterCellBroadcastMsg: function unregisterCellBroadcastMsg(listener) {
-    this.unregisterListener("_cellBroadcastListeners", listener);
+    //TODO: Bug 921326 - Cellbroadcast API: support multiple sim cards
+    this.unregisterListener("_cellBroadcastListeners", 0, listener);
   },
 
   registerIccMsg: function registerIccMsg(listener) {
     debug("Registering for ICC related messages");
-    this.registerListener("_iccListeners", listener);
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    this.registerListener("_iccListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
   },
 
   unregisterIccMsg: function unregisterIccMsg(listener) {
-    this.unregisterListener("_iccListeners", listener);
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    this.unregisterListener("_iccListeners", 0, listener);
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     if (topic == "xpcom-shutdown") {
       this.destroyDOMRequestHelper();
       Services.obs.removeObserver(this, "xpcom-shutdown");
@@ -1473,64 +1513,72 @@ RILContentHelper.prototype = {
     Services.DOMRequest.fireDetailedError(request, detailedError);
   },
 
   receiveMessage: function receiveMessage(msg) {
     let request;
     debug("Received message '" + msg.name + "': " + JSON.stringify(msg.json));
 
     let data = msg.json.data;
+    let clientId = msg.json.clientId;
     switch (msg.name) {
       case "RIL:CardStateChanged":
-        if (this.rilContext.cardState != data.cardState) {
-          this.rilContext.cardState = data.cardState;
-          this._deliverEvent("_iccListeners",
+        if (this.rilContexts[clientId].cardState != data.cardState) {
+          this.rilContexts[clientId].cardState = data.cardState;
+          this._deliverEvent(clientId,
+                             "_iccListeners",
                              "notifyCardStateChanged",
                              null);
         }
         break;
       case "RIL:IccInfoChanged":
-        this.updateIccInfo(data);
-        this._deliverEvent("_iccListeners", "notifyIccInfoChanged", null);
+        this.updateIccInfo(clientId, data);
+        this._deliverEvent(clientId,
+                           "_iccListeners",
+                           "notifyIccInfoChanged",
+                           null);
         break;
       case "RIL:VoiceInfoChanged":
         this.updateConnectionInfo(data,
-                                  this.rilContext.voiceConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners",
+                                  this.rilContexts[clientId].voiceConnectionInfo);
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyVoiceChanged",
                            null);
         break;
       case "RIL:DataInfoChanged":
         this.updateConnectionInfo(data,
-                                  this.rilContext.dataConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners",
+                                  this.rilContexts[clientId].dataConnectionInfo);
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyDataChanged",
                            null);
         break;
       case "RIL:OtaStatusChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyOtaStatusChanged",
                            [data]);
         break;
       case "RIL:GetAvailableNetworks":
         this.handleGetAvailableNetworks(data);
         break;
       case "RIL:NetworkSelectionModeChanged":
-        this.rilContext.networkSelectionMode = data.mode;
+        this.rilContexts[clientId].networkSelectionMode = data.mode;
         break;
       case "RIL:SelectNetwork":
-        this.handleSelectNetwork(data,
+        this.handleSelectNetwork(clientId, data,
                                  RIL.GECKO_NETWORK_SELECTION_MANUAL);
         break;
       case "RIL:SelectNetworkAuto":
-        this.handleSelectNetwork(data,
+        this.handleSelectNetwork(clientId, data,
                                  RIL.GECKO_NETWORK_SELECTION_AUTOMATIC);
         break;
       case "RIL:VoicemailNotification":
-        this.handleVoicemailNotification(data);
+        this.handleVoicemailNotification(clientId, data);
         break;
       case "RIL:VoicemailInfoChanged":
         this.updateInfo(data, this.voicemailInfo);
         break;
       case "RIL:CardLockResult": {
         let requestId = data.requestId;
         let requestWindow = this._windowsMap[requestId];
         delete this._windowsMap[requestId];
@@ -1555,30 +1603,31 @@ RILContentHelper.prototype = {
         if (data.success) {
           let result = new MobileIccCardLockRetryCount(data);
           this.fireRequestSuccess(data.requestId, result);
         } else {
           this.fireRequestError(data.requestId, data.errorMsg);
         }
         break;
       case "RIL:USSDReceived":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyUssdReceived",
                            [data.message, data.sessionEnded]);
         break;
       case "RIL:SendMMI":
       case "RIL:CancelMMI":
         this.handleSendCancelMMI(data);
         break;
       case "RIL:StkCommand":
-        this._deliverEvent("_iccListeners", "notifyStkCommand",
+        this._deliverEvent(clientId, "_iccListeners", "notifyStkCommand",
                            [JSON.stringify(data)]);
         break;
       case "RIL:StkSessionEnd":
-        this._deliverEvent("_iccListeners", "notifyStkSessionEnd", null);
+        this._deliverEvent(clientId, "_iccListeners", "notifyStkSessionEnd", null);
         break;
       case "RIL:IccOpenChannel":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.channel);
         break;
       case "RIL:IccCloseChannel":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
@@ -1587,74 +1636,77 @@ RILContentHelper.prototype = {
         break;
       case "RIL:ReadIccContacts":
         this.handleReadIccContacts(data);
         break;
       case "RIL:UpdateIccContact":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:DataError":
-        this.updateConnectionInfo(data, this.rilContext.dataConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners", "notifyDataError",
+        this.updateConnectionInfo(data, this.rilContexts[clientId].dataConnectionInfo);
+        this._deliverEvent(clientId, "_mobileConnectionListeners", "notifyDataError",
                            [data.errorMsg]);
         break;
-      case "RIL:GetCallForwardingOption":
-        this.handleGetCallForwardingOption(data);
+      case "RIL:GetCallForwardingOptions":
+        this.handleGetCallForwardingOptions(data);
         break;
-      case "RIL:SetCallForwardingOption":
+      case "RIL:SetCallForwardingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
-      case "RIL:GetCallBarringOption":
-        this.handleGetCallBarringOption(data);
+      case "RIL:GetCallBarringOptions":
+        this.handleGetCallBarringOptions(data);
         break;
-      case "RIL:SetCallBarringOption":
+      case "RIL:SetCallBarringOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:ChangeCallBarringPassword":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
-      case "RIL:GetCallWaitingOption":
+      case "RIL:GetCallWaitingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.enabled);
         break;
-      case "RIL:SetCallWaitingOption":
+      case "RIL:SetCallWaitingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:CfStateChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyCFStateChange",
                            [data.success, data.action,
                             data.reason, data.number,
                             data.timeSeconds, data.serviceClass]);
         break;
       case "RIL:GetCallingLineIdRestriction":
         this.handleGetCallingLineIdRestriction(data);
         break;
       case "RIL:SetCallingLineIdRestriction":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:CellBroadcastReceived": {
         let message = new CellBroadcastMessage(data);
-        this._deliverEvent("_cellBroadcastListeners",
+        this._deliverEvent(clientId,
+                           "_cellBroadcastListeners",
                            "notifyMessageReceived",
                            [message]);
         break;
       }
       case "RIL:SetRoamingPreference":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:GetRoamingPreference":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.mode);
         break;
       case "RIL:ExitEmergencyCbMode":
         this.handleExitEmergencyCbMode(data);
         break;
       case "RIL:EmergencyCbModeChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyEmergencyCbModeChanged",
                            [data.active, data.timeoutMs]);
         break;
       case "RIL:SetVoicePrivacyMode":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:GetVoicePrivacyMode":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
@@ -1685,19 +1737,19 @@ RILContentHelper.prototype = {
       let info = new MobileNetworkInfo();
       this.updateInfo(network, info);
       networks[i] = info;
     }
 
     this.fireRequestSuccess(message.requestId, networks);
   },
 
-  handleSelectNetwork: function handleSelectNetwork(message, mode) {
-    this._selectingNetwork = null;
-    this.rilContext.networkSelectionMode = mode;
+  handleSelectNetwork: function handleSelectNetwork(clientId, message, mode) {
+    this._selectingNetworks[clientId] = null;
+    this.rilContexts[clientId].networkSelectionMode = mode;
 
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
     } else {
       this.fireRequestSuccess(message.requestId, null);
     }
   },
 
@@ -1737,17 +1789,18 @@ RILContentHelper.prototype = {
       contact.id = message.iccid + c.recordId;
       return contact;
     });
 
     this.fireRequestSuccess(message.requestId,
                             ObjectWrapper.wrap(result, window));
   },
 
-  handleVoicemailNotification: function handleVoicemailNotification(message) {
+  handleVoicemailNotification: function handleVoicemailNotification(clientId, message) {
+    // TODO: Bug 818352 - B2G Multi-SIM: voicemail - add subscription id in nsIRILContentHelper
     let changed = false;
     if (!this.voicemailStatus) {
       this.voicemailStatus = new VoicemailStatus();
     }
 
     if (this.voicemailStatus.hasMessages != message.active) {
       changed = true;
       this.voicemailStatus.hasMessages = message.active;
@@ -1767,47 +1820,48 @@ RILContentHelper.prototype = {
     }
 
     if (this.voicemailStatus.returnMessage != message.returnMessage) {
       changed = true;
       this.voicemailStatus.returnMessage = message.returnMessage;
     }
 
     if (changed) {
-      this._deliverEvent("_voicemailListeners",
+      this._deliverEvent(clientId,
+                         "_voicemailListeners",
                          "notifyStatusChanged",
                          [this.voicemailStatus]);
     }
   },
 
   _cfRulesToMobileCfInfo: function _cfRulesToMobileCfInfo(rules) {
     for (let i = 0; i < rules.length; i++) {
       let rule = rules[i];
       let info = new MobileCFInfo();
       this.updateInfo(rule, info);
       rules[i] = info;
     }
   },
 
-  handleGetCallForwardingOption: function handleGetCallForwardingOption(message) {
+  handleGetCallForwardingOptions: function handleGetCallForwardingOptions(message) {
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
       return;
     }
 
     this._cfRulesToMobileCfInfo(message.rules);
     this.fireRequestSuccess(message.requestId, message.rules);
   },
 
-  handleGetCallBarringOption: function handleGetCallBarringOption(message) {
+  handleGetCallBarringOptions: function handleGetCallBarringOptions(message) {
     if (!message.success) {
       this.fireRequestError(message.requestId, message.errorMsg);
     } else {
-      let option = new CallBarringOption(message);
-      this.fireRequestSuccess(message.requestId, option);
+      let options = new CallBarringOptions(message);
+      this.fireRequestSuccess(message.requestId, options);
     }
   },
 
   handleGetCallingLineIdRestriction:
     function handleGetCallingLineIdRestriction(message) {
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
       return;
@@ -1873,18 +1927,21 @@ RILContentHelper.prototype = {
       let mmiError = new requestWindow.DOMMMIError(result.serviceCode,
                                                    message.errorMsg,
                                                    null,
                                                    result.additionalInformation);
       Services.DOMRequest.fireDetailedError(request, mmiError);
     }
   },
 
-  _deliverEvent: function _deliverEvent(listenerType, name, args) {
-    let thisListeners = this[listenerType];
+  _deliverEvent: function _deliverEvent(clientId, listenerType, name, args) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let thisListeners = this[listenerType][clientId];
     if (!thisListeners) {
       return;
     }
 
     let listeners = thisListeners.slice();
     for (let listener of listeners) {
       if (thisListeners.indexOf(listener) == -1) {
         continue;
@@ -1945,28 +2002,28 @@ RILContentHelper.prototype = {
       case Ci.nsIDOMMozMobileConnection.CALL_BARRING_PROGRAM_INCOMING_ROAMING:
         return true;
       default:
         return false;
     }
   },
 
   /**
-   * Helper for guarding us against invalid option for call barring.
+   * Helper for guarding us against invalid options for call barring.
    */
-  _isValidCallBarringOption:
-      function _isValidCallBarringOption(option, usedForSetting) {
-    if (!option ||
-        option.serviceClass == null ||
-        !this._isValidCallBarringProgram(option.program)) {
+  _isValidCallBarringOptions:
+      function _isValidCallBarringOptions(options, usedForSetting) {
+    if (!options ||
+        options.serviceClass == null ||
+        !this._isValidCallBarringProgram(options.program)) {
       return false;
     }
 
-    // For setting callbarring option, |enabled| and |password| are required.
-    if (usedForSetting && (option.enabled == null || option.password == null)) {
+    // For setting callbarring options, |enabled| and |password| are required.
+    if (usedForSetting && (options.enabled == null || options.password == null)) {
       return false;
     }
 
     return true;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILContentHelper,
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -85,23 +85,23 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES
   "RIL:GetNumRadioInterfaces",
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:RegisterMobileConnectionMsg",
-  "RIL:SetCallForwardingOption",
-  "RIL:GetCallForwardingOption",
-  "RIL:SetCallBarringOption",
-  "RIL:GetCallBarringOption",
+  "RIL:SetCallForwardingOptions",
+  "RIL:GetCallForwardingOptions",
+  "RIL:SetCallBarringOptions",
+  "RIL:GetCallBarringOptions",
   "RIL:ChangeCallBarringPassword",
-  "RIL:SetCallWaitingOption",
-  "RIL:GetCallWaitingOption",
+  "RIL:SetCallWaitingOptions",
+  "RIL:GetCallWaitingOptions",
   "RIL:SetCallingLineIdRestriction",
   "RIL:GetCallingLineIdRestriction",
   "RIL:SetRoamingPreference",
   "RIL:GetRoamingPreference",
   "RIL:ExitEmergencyCbMode",
   "RIL:SetVoicePrivacyMode",
   "RIL:GetVoicePrivacyMode"
 ];
@@ -930,35 +930,35 @@ RadioInterface.prototype = {
         this.workerMessenger.sendWithIPCMessage(msg, "iccExchangeAPDU");
         break;
       case "RIL:ReadIccContacts":
         this.workerMessenger.sendWithIPCMessage(msg, "readICCContacts");
         break;
       case "RIL:UpdateIccContact":
         this.workerMessenger.sendWithIPCMessage(msg, "updateICCContact");
         break;
-      case "RIL:SetCallForwardingOption":
-        this.setCallForwardingOption(msg.target, msg.json.data);
+      case "RIL:SetCallForwardingOptions":
+        this.setCallForwardingOptions(msg.target, msg.json.data);
         break;
-      case "RIL:GetCallForwardingOption":
+      case "RIL:GetCallForwardingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallForwardStatus");
         break;
-      case "RIL:SetCallBarringOption":
+      case "RIL:SetCallBarringOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "setCallBarring");
         break;
-      case "RIL:GetCallBarringOption":
+      case "RIL:GetCallBarringOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallBarringStatus");
         break;
       case "RIL:ChangeCallBarringPassword":
         this.workerMessenger.sendWithIPCMessage(msg, "changeCallBarringPassword");
         break;
-      case "RIL:SetCallWaitingOption":
+      case "RIL:SetCallWaitingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "setCallWaiting");
         break;
-      case "RIL:GetCallWaitingOption":
+      case "RIL:GetCallWaitingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallWaiting");
         break;
       case "RIL:SetCallingLineIdRestriction":
         this.setCallingLineIdRestriction(msg.target, msg.json.data);
         break;
       case "RIL:GetCallingLineIdRestriction":
         this.workerMessenger.sendWithIPCMessage(msg, "getCLIR");
         break;
@@ -2401,22 +2401,22 @@ RadioInterface.prototype = {
       target.sendAsyncMessage("RIL:SendMMI", {
         clientId: this.clientId,
         data: response
       });
       return false;
     }).bind(this));
   },
 
-  setCallForwardingOption: function setCallForwardingOption(target, message) {
-    if (DEBUG) this.debug("setCallForwardingOption: " + JSON.stringify(message));
+  setCallForwardingOptions: function setCallForwardingOptions(target, message) {
+    if (DEBUG) this.debug("setCallForwardingOptions: " + JSON.stringify(message));
     message.serviceClass = RIL.ICC_SERVICE_CLASS_VOICE;
     this.workerMessenger.send("setCallForward", message, (function(response) {
       this._sendCfStateChanged(response);
-      target.sendAsyncMessage("RIL:SetCallForwardingOption", {
+      target.sendAsyncMessage("RIL:SetCallForwardingOptions", {
         clientId: this.clientId,
         data: response
       });
       return false;
     }).bind(this));
   },
 
   setCallingLineIdRestriction: function setCallingLineIdRestriction(target,
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -21,16 +21,17 @@ const WIFIWORKER_CID        = Components
 
 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 
 const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
 const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+const MAX_RETRIES_ON_DHCP_FAILURE = 2;
 
 // Settings DB path for wifi
 const SETTINGS_WIFI_ENABLED            = "wifi.enabled";
 const SETTINGS_WIFI_DEBUG_ENABLED      = "wifi.debugging.enabled";
 // Settings DB path for Wifi tethering.
 const SETTINGS_WIFI_TETHERING_ENABLED  = "tethering.wifi.enabled";
 const SETTINGS_WIFI_SSID               = "tethering.wifi.ssid";
 const SETTINGS_WIFI_SECURITY_TYPE      = "tethering.wifi.security.type";
@@ -553,16 +554,31 @@ var WifiManager = (function() {
           (key in staticIpConfig) &&
           staticIpConfig[key].enabled) {
           debug("Run static ip");
           runStaticIp(manager.ifname, key);
           return;
       }
       netUtil.runDhcp(manager.ifname, function(data) {
         dhcpInfo = data.info;
+        if (!dhcpInfo) {
+          if (++manager.dhcpFailuresCount >= MAX_RETRIES_ON_DHCP_FAILURE) {
+            manager.dhcpFailuresCount = 0;
+            notify("disconnected", {ssid: manager.connectionInfo.ssid});
+            return;
+          }
+          // NB: We have to call disconnect first. Otherwise, we only reauth with
+          // the existing AP and don't retrigger DHCP.
+          manager.disconnect(function() {
+            manager.reassociate(function(){});
+          });
+          return;
+        }
+
+        manager.dhcpFailuresCount = 0;
         notify("networkconnected", data);
       });
     });
   }
 
   var supplicantStatesMap = (sdkVersion >= 15) ?
     ["DISCONNECTED", "INTERFACE_DISABLED", "INACTIVE", "SCANNING",
      "AUTHENTICATING", "ASSOCIATING", "ASSOCIATED", "FOUR_WAY_HANDSHAKE",
@@ -784,16 +800,17 @@ var WifiManager = (function() {
   // Initial state.
   manager.state = "UNINITIALIZED";
   manager.tetheringState = "UNINITIALIZED";
   manager.enabled = false;
   manager.supplicantStarted = false;
   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   manager.authenticationFailuresCount = 0;
   manager.loopDetectionCount = 0;
+  manager.dhcpFailuresCount = 0;
 
   var waitForDriverReadyTimer = null;
   function cancelWaitForDriverReadyTimer() {
     if (waitForDriverReadyTimer) {
       waitForDriverReadyTimer.cancel();
       waitForDriverReadyTimer = null;
     }
   };
@@ -1818,44 +1835,41 @@ function WifiWorker() {
         // about getting stuck while scanning.
         if (!WifiManager.backgroundScanEnabled && WifiManager.enabled)
           startScanStuckTimer();
         break;
     }
   };
 
   WifiManager.onnetworkconnected = function() {
-    if (this.info) {
-      WifiNetworkInterface.state =
-        Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
-      WifiNetworkInterface.ip = this.info.ipaddr_str;
-      WifiNetworkInterface.netmask = this.info.mask_str;
-      WifiNetworkInterface.broadcast = this.info.broadcast_str;
-      WifiNetworkInterface.gateway = this.info.gateway_str;
-      WifiNetworkInterface.dns1 = this.info.dns1_str;
-      WifiNetworkInterface.dns2 = this.info.dns2_str;
-      Services.obs.notifyObservers(WifiNetworkInterface,
-                                   kNetworkInterfaceStateChangedTopic,
-                                   null);
-
-      self.ipAddress = this.info.ipaddr_str;
-
-      // We start the connection information timer when we associate, but
-      // don't have our IP address until here. Make sure that we fire a new
-      // connectionInformation event with the IP address the next time the
-      // timer fires.
-      self._lastConnectionInfo = null;
-      self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
-    } else {
-      // NB: We have to call disconnect first. Otherwise, we only reauth with
-      // the existing AP and don't retrigger DHCP.
-      WifiManager.disconnect(function() {
-        WifiManager.reassociate(function(){});
-      });
+    if (!this.info) {
+      debug("Network information is invalid.");
+      return;
     }
+
+    WifiNetworkInterface.state =
+      Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
+    WifiNetworkInterface.ip = this.info.ipaddr_str;
+    WifiNetworkInterface.netmask = this.info.mask_str;
+    WifiNetworkInterface.broadcast = this.info.broadcast_str;
+    WifiNetworkInterface.gateway = this.info.gateway_str;
+    WifiNetworkInterface.dns1 = this.info.dns1_str;
+    WifiNetworkInterface.dns2 = this.info.dns2_str;
+    Services.obs.notifyObservers(WifiNetworkInterface,
+                                 kNetworkInterfaceStateChangedTopic,
+                                 null);
+
+    self.ipAddress = this.info.ipaddr_str;
+
+    // We start the connection information timer when we associate, but
+    // don't have our IP address until here. Make sure that we fire a new
+    // connectionInformation event with the IP address the next time the
+    // timer fires.
+    self._lastConnectionInfo = null;
+    self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
   };
 
   WifiManager.onscanresultsavailable = function() {
     if (self._scanStuckTimer) {
       // We got scan results! We must not be stuck for now, try again.
       self._scanStuckTimer.cancel();
       self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
                                             Ci.nsITimer.TYPE_ONE_SHOT);