Bug 783392 - RadioInterfaceLayer broadcasts radio-state information to all content processes. r=marshall_law
authorHsin-Yi Tsai <htsai@mozilla.com>
Tue, 09 Oct 2012 18:07:11 +0800
changeset 110750 9098fb489c6e8556e58bab376b81eff405941e73
parent 110749 96184c897bfcb26e64dd7d0c434bbaa4b231b019
child 110751 11665365c7c07cc383d1d3c5fb962e62df7300a5
push id23712
push useremorley@mozilla.com
push dateFri, 19 Oct 2012 14:23:49 +0000
treeherdermozilla-central@7fcac3016159 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarshall_law
bugs783392
milestone19.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
Bug 783392 - RadioInterfaceLayer broadcasts radio-state information to all content processes. r=marshall_law
dom/network/interfaces/nsIMobileConnectionProvider.idl
dom/network/src/MobileConnection.cpp
dom/system/gonk/RILContentHelper.js
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/nsIRadioInterfaceLayer.idl
dom/telephony/Telephony.cpp
dom/telephony/Voicemail.cpp
--- a/dom/network/interfaces/nsIMobileConnectionProvider.idl
+++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl
@@ -9,19 +9,29 @@ interface nsIDOMMozMobileConnectionInfo;
 interface nsIDOMMozMobileNetworkInfo;
 interface nsIDOMDOMRequest;
 interface nsIDOMWindow;
 
 /**
  * XPCOM component (in the content process) that provides the mobile
  * network information.
  */
-[scriptable, uuid(bc1a82aa-2a1f-4a27-a5e7-614d06b72e0a)]
+[scriptable, uuid(3db24bfe-abc4-43a3-9183-07e516a33af9)]
 interface nsIMobileConnectionProvider : nsISupports
 {
+  /**
+   * Called when a content process registers receiving unsolicited messages from
+   * RadioInterfaceLayer in the chrome process. Only a content granted
+   * mobileconnection permission is allowed to register. Note that a content
+   * doesn't need to unregister because the chrome process will remove it from
+   * the registration list once the chrome receives a 'child-process-shutdown'
+   * message.
+   */
+  void registerMobileConnectionMsg();
+
   readonly attribute DOMString cardState;
   readonly attribute nsIDOMMozMobileICCInfo iccInfo;
   readonly attribute nsIDOMMozMobileConnectionInfo voiceConnectionInfo;
   readonly attribute nsIDOMMozMobileConnectionInfo dataConnectionInfo;
   readonly attribute DOMString networkSelectionMode;
 
   nsIDOMDOMRequest getNetworks(in nsIDOMWindow window);
   nsIDOMDOMRequest selectNetwork(in nsIDOMWindow window, in nsIDOMMozMobileNetworkInfo network);
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -77,16 +77,18 @@ NS_IMPL_EVENT_HANDLER(MobileConnection, 
 MobileConnection::MobileConnection()
 {
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   // 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!");
+  } else {
+    mProvider->RegisterMobileConnectionMsg();
   }
 }
 
 void
 MobileConnection::Init(nsPIDOMWindow* aWindow)
 {
   BindToOwner(aWindow);
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -509,16 +509,31 @@ RILContentHelper.prototype = {
   registerVoicemailCallback: function registerVoicemailCallback(callback) {
     this.registerCallback("_voicemailCallbacks", callback);
   },
 
   unregisterVoicemailCallback: function unregisteVoicemailCallback(callback) {
     this.unregisterCallback("_voicemailCallbacks", callback);
   },
 
+  registerTelephonyMsg: function registerTelephonyMsg() {
+    debug("Registering for telephony-related messages");
+    cpmm.sendAsyncMessage("RIL:RegisterTelephonyMsg");
+  },
+
+  registerMobileConnectionMsg: function registerMobileConnectionMsg() {
+    debug("Registering for mobile connection-related messages");
+    cpmm.sendAsyncMessage("RIL:RegisterMobileConnectionMsg");
+  },
+
+  registerVoicemailMsg: function registerVoicemailMsg() {
+    debug("Registering for voicemail-related messages");
+    cpmm.sendAsyncMessage("RIL:RegisterVoicemailMsg");
+  },
+
   enumerateCalls: function enumerateCalls(callback) {
     debug("Requesting enumeration of calls for callback: " + callback);
     // We need 'requestId' to meet the 'RILContentHelper <--> RadioInterfaceLayer'
     // protocol.
     let requestId = this._getRandomId();
     cpmm.sendAsyncMessage("RIL:EnumerateCalls", {requestId: requestId});
     if (!this._enumerationTelephonyCallbacks) {
       this._enumerationTelephonyCallbacks = [];
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -54,31 +54,37 @@ const RIL_IPC_TELEPHONY_MSG_NAMES = [
   "RIL:StopTone",
   "RIL:Dial",
   "RIL:DialEmergency",
   "RIL:HangUp",
   "RIL:AnswerCall",
   "RIL:RejectCall",
   "RIL:HoldCall",
   "RIL:ResumeCall",
+  "RIL:RegisterTelephonyMsg"
 ];
 
 const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:GetCardLock",
   "RIL:UnlockCardLock",
   "RIL:SetCardLock",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:SendStkResponse",
   "RIL:SendStkMenuSelection",
   "RIL:SendStkEventDownload",
+  "RIL:RegisterMobileConnectionMsg"
+];
+
+const RIL_IPC_VOICEMAIL_MSG_NAMES = [
+  "RIL:RegisterVoicemailMsg"
 ];
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/smsservice;1",
                                    "nsISmsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
                                    "@mozilla.org/sms/smsrequestmanager;1",
@@ -246,22 +252,30 @@ function RadioInterfaceLayer() {
   lock.get("ril.callwaiting.enabled", this);
 
   // Read the 'time.nitz.automatic-update.enabled' setting to see if
   // we need to adjust the system clock time and time zone by NITZ.
   lock.get(kTimeNitzAutomaticUpdateEnabled, this);
 
   this._messageManagerByRequest = {};
 
+  // Manage message targets in terms of permission. Only the authorized and
+  // registered contents can receive related messages.
+  this._messageManagerByPermission = {};
+
+  ppmm.addMessageListener("child-process-shutdown", this);
   for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) {
     ppmm.addMessageListener(msgname, this);
   }
   for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
     ppmm.addMessageListener(msgname, this);
   }
+  for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
+    ppmm.addMessageListener(msgname, this);
+  }
   Services.obs.addObserver(this, "xpcom-shutdown", false);
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
 
   this._sentSmsEnvelopes = {};
 
   this.portAddressedSmsApps = {};
   this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
@@ -279,28 +293,44 @@ RadioInterfaceLayer.prototype = {
                                          Ci.nsIObserver,
                                          Ci.nsISettingsServiceCallback]),
 
   /**
    * Process a message from the content process.
    */
   receiveMessage: function receiveMessage(msg) {
     debug("Received '" + msg.name + "' message from content process");
+    if (msg.name == "child-process-shutdown") {
+      // By the time we receive child-process-shutdown, the child process has
+      // already forgotten its permissions so we need to unregister for both
+      // telephony and mobile connection messages.
+      this.unregisterTelephonyTarget(msg.target);
+      this.unregisterMobileConnectionTarget(msg.target);
+      this.unregisterVoicemailTarget(msg.target);
+      return;
+    }
+
     if (RIL_IPC_TELEPHONY_MSG_NAMES.indexOf(msg.name) != -1) {
       if (!msg.target.assertPermission("telephony")) {
         debug("Telephony message " + msg.name +
               " from a content process with no 'telephony' privileges.");
         return null;
       }
     } else if (RIL_IPC_MOBILECONNECTION_MSG_NAMES.indexOf(msg.name) != -1) {
       if (!msg.target.assertPermission("mobileconnection")) {
         debug("MobileConnection message " + msg.name +
               " from a content process with no 'mobileconnection' privileges.");
         return null;
       }
+    } else if (RIL_IPC_VOICEMAIL_MSG_NAMES.indexOf(msg.name) != -1) {
+      if (!msg.target.assertPermission("voicemail")) {
+        debug("Voicemail message " + msg.name +
+              " from a content process with no 'voicemail' privileges.");
+        return null;
+      }
     } else {
       debug("Ignoring unknown message type: " + msg.name);
       return null;
     }
 
     switch (msg.name) {
       case "RIL:GetRilContext":
         // This message is sync.
@@ -343,27 +373,31 @@ RadioInterfaceLayer.prototype = {
         this.rejectCall(msg.json);
         break;
       case "RIL:HoldCall":
         this.holdCall(msg.json);
         break;
       case "RIL:ResumeCall":
         this.resumeCall(msg.json);
         break;
+      case "RIL:RegisterTelephonyMsg":
+        this.registerTelephonyTarget(msg.target);
+        break;
       case "RIL:GetAvailableNetworks":
         this.saveRequestTarget(msg);
         this.getAvailableNetworks(msg.json.requestId);
         break;
       case "RIL:SelectNetwork":
         this.saveRequestTarget(msg);
         this.selectNetwork(msg.json);
         break;
       case "RIL:SelectNetworkAuto":
         this.saveRequestTarget(msg);
         this.selectNetworkAuto(msg.json.requestId);
+        break;
       case "RIL:GetCardLock":
         this.saveRequestTarget(msg);
         this.getCardLock(msg.json);
         break;
       case "RIL:UnlockCardLock":
         this.saveRequestTarget(msg);
         this.unlockCardLock(msg.json);
         break;
@@ -383,16 +417,22 @@ RadioInterfaceLayer.prototype = {
         this.sendStkResponse(msg.json);
         break;
       case "RIL:SendStkMenuSelection":
         this.sendStkMenuSelection(msg.json);
         break;
       case "RIL:SendStkEventDownload":
         this.sendStkEventDownload(msg.json);
         break;
+      case "RIL:RegisterMobileConnectionMsg":
+        this.registerMobileConnectionTarget(msg.target);
+        break;
+      case "RIL:RegisterVoicemailMsg":
+        this.registerVoicemailTarget(msg.target);
+        break;
     }
   },
 
   onerror: function onerror(event) {
     debug("Got an error: " + event.filename + ":" +
           event.lineno + ": " + event.message + "\n");
     event.preventDefault();
   },
@@ -453,17 +493,17 @@ RadioInterfaceLayer.prototype = {
       case "operatorchange":
         this.handleOperatorChange(message);
         break;
       case "radiostatechange":
         this.handleRadioStateChange(message);
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
-        ppmm.broadcastAsyncMessage("RIL:CardStateChanged", message);
+        this._sendMobileConnectionMessage("RIL:CardStateChanged", message);
         break;
       case "setCallWaiting":
         this.handleCallWaitingStatusChange(message);
         break;
       case "sms-received":
         this.handleSmsReceived(message);
         return;
       case "sms-sent":
@@ -500,17 +540,17 @@ RadioInterfaceLayer.prototype = {
         if (callback) {
           delete this._contactsCallbacks[message.requestId];
           callback.receiveContactsList(message.errorMsg,
                                        message.contactType,
                                        message.contacts);
         }
         break;
       case "iccmbdn":
-        ppmm.broadcastAsyncMessage("RIL:VoicemailNumberChanged", message);
+        this._sendVoicemailMessage("RIL:VoicemailNumberChanged", message);
         break;
       case "USSDReceived":
         debug("USSDReceived " + JSON.stringify(message));
         this.handleUSSDReceived(message);
         break;
       case "sendMMI":
       case "sendUSSD":
         this.handleSendMMI(message);
@@ -518,17 +558,17 @@ RadioInterfaceLayer.prototype = {
       case "cancelMMI":
       case "cancelUSSD":
         this.handleCancelMMI(message);
         break;
       case "stkcommand":
         this.handleStkProactiveCommand(message);
         break;
       case "stksessionend":
-        ppmm.broadcastAsyncMessage("RIL:StkSessionEnd", null);
+        this._sendMobileConnectionMessage("RIL:StkSessionEnd", null);
         break;
       case "setPreferredNetworkType":
         this.handleSetPreferredNetworkType(message);
         break;
       case "stkcallsetup":
         // A call has been placed by the STK app. We shall launch the dialer
         // app by sending a system message, in order to further control the
         // call, e.g. hang it up.
@@ -559,16 +599,96 @@ RadioInterfaceLayer.prototype = {
 
     if (!target) {
       return;
     }
 
     target.sendAsyncMessage(requestType, options);
   },
 
+  _messageManagerByPermission: null,
+  registerMessageTarget: function registerMessageTarget(permission, target) {
+    let targets = this._messageManagerByPermission[permission];
+    if (!targets) {
+      targets = this._messageManagerByPermission[permission] = [];
+    }
+
+    if (targets.indexOf(target) != -1) {
+      debug("Already registered this target!");
+      return;
+    }
+
+    targets.push(target);
+    debug("Registered " + permission + " target: " + target);
+  },
+
+  unregisterMessageTarget: function unregisterMessageTarget(permission, target) {
+    let targets = this._messageManagerByPermission[permission];
+    if (!targets) {
+      return;
+    }
+
+    let index = targets.indexOf(target);
+    if (index != -1) {
+      targets.splice(index, 1);
+      debug("Unregistered " + permission + " target: " + target);
+    }
+  },
+
+  registerTelephonyTarget: function registerTelephonyTarget(target) {
+    this.registerMessageTarget("telephony", target);
+  },
+
+  registerMobileConnectionTarget: function registerMobileConnectionTarget(target) {
+    this.registerMessageTarget("mobileconnection", target);
+  },
+
+  registerVoicemailTarget: function registerVoicemailTarget(target) {
+    this.registerMessageTarget("voicemail", target);
+  },
+
+  unregisterTelephonyTarget: function unregisterTelephonyTarget(target) {
+    this.unregisterMessageTarget("telephony", target);
+  },
+
+  unregisterMobileConnectionTarget: function unregisterMobileConnectionTarget(target) {
+    this.unregisterMessageTarget("mobileconnection", target);
+  },
+
+  unregisterVoicemailTarget: function unregisterVoicemailTarget(target) {
+    this.unregisterMessageTarget("voicemail", target);
+  },
+
+  _sendTargetMessage: function _sendTargetMessage(permission, message, options) {
+    let thisTargets = this._messageManagerByPermission[permission];
+    if (!thisTargets) {
+      return;
+    }
+
+    let targets = thisTargets.slice();
+    for each (let target in targets) {
+      if (thisTargets.indexOf(target) == -1) {
+        continue;
+      }
+      target.sendAsyncMessage(message, options);
+    }
+  },
+
+  _sendTelephonyMessage: function sendTelephonyMessage(message, options) {
+    this._sendTargetMessage("telephony", message, options);
+  },
+
+  _sendMobileConnectionMessage: function sendMobileConnectionMessage(message, options) {
+    this._sendTargetMessage("mobileconnection", message, options);
+  },
+
+  _sendVoicemailMessage: function sendVoicemailMessage(message, options) {
+    this._sendTargetMessage("voicemail", message, options);
+  },
+
   updateNetworkInfo: function updateNetworkInfo(message) {
     let voiceMessage = message[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
     let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
     let operatorMessage = message[RIL.NETWORK_INFO_OPERATOR];
     let selectionMessage = message[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
 
     // Batch the *InfoChanged messages together
     if (voiceMessage) {
@@ -592,20 +712,20 @@ RadioInterfaceLayer.prototype = {
         data.network = operatorMessage;
       }
     }
 
     this.checkRoamingBetweenOperators(voice);
     this.checkRoamingBetweenOperators(data);
 
     if (voiceMessage || operatorMessage) {
-      ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voice);
+      this._sendMobileConnectionMessage("RIL:VoiceInfoChanged", voice);
     }
     if (dataMessage || operatorMessage) {
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", data);
+      this._sendMobileConnectionMessage("RIL:DataInfoChanged", data);
     }
 
     if (selectionMessage) {
       this.updateNetworkSelectionMode(selectionMessage);
     }
   },
 
   /**
@@ -669,17 +789,17 @@ RadioInterfaceLayer.prototype = {
     let newCell = newInfo.cell;
     if ((newCell.gsmLocationAreaCode < 0) || (newCell.gsmCellId < 0)) {
       voiceInfo.cell = null;
     } else {
       voiceInfo.cell = newCell;
     }
 
     if (!newInfo.batch) {
-      ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
+      this._sendMobileConnectionMessage("RIL:VoiceInfoChanged", voiceInfo);
     }
   },
 
   updateDataConnection: function updateDataConnection(newInfo) {
     let dataInfo = this.rilContext.data;
     dataInfo.state = newInfo.state;
     dataInfo.roaming = newInfo.roaming;
     dataInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
@@ -699,28 +819,28 @@ RadioInterfaceLayer.prototype = {
     let newCell = newInfo.cell;
     if ((newCell.gsmLocationAreaCode < 0) || (newCell.gsmCellId < 0)) {
       dataInfo.cell = null;
     } else {
       dataInfo.cell = newCell;
     }
 
     if (!newInfo.batch) {
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", dataInfo);
+      this._sendMobileConnectionMessage("RIL:DataInfoChanged", dataInfo);
     }
     this.updateRILNetworkInterface();
   },
 
   /**
    * Handle data errors
    */
   handleDataCallError: function handleDataCallError(message) {
     // Notify data call error only for data APN
     if (message.apn == this.dataCallSettings["apn"]) {
-      ppmm.broadcastAsyncMessage("RIL:DataError", message);
+      this._sendMobileConnectionMessage("RIL:DataError", message);
     }
 
     this._deliverDataCallCallback("dataCallError", [message]);
   },
 
   _preferredNetworkType: null,
   setPreferredNetworkType: function setPreferredNetworkType(value) {
     let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(value);
@@ -756,25 +876,25 @@ RadioInterfaceLayer.prototype = {
 
   handleSignalStrengthChange: function handleSignalStrengthChange(message) {
     let voiceInfo = this.rilContext.voice;
     // TODO CDMA, EVDO, LTE, etc. (see bug 726098)
     if (voiceInfo.signalStrength != message.gsmDBM ||
         voiceInfo.relSignalStrength != message.gsmRelative) {
       voiceInfo.signalStrength = message.gsmDBM;
       voiceInfo.relSignalStrength = message.gsmRelative;
-      ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
+      this._sendMobileConnectionMessage("RIL:VoiceInfoChanged", voiceInfo);
     }
 
     let dataInfo = this.rilContext.data;
     if (dataInfo.signalStrength != message.gsmDBM ||
         dataInfo.relSignalStrength != message.gsmRelative) {
       dataInfo.signalStrength = message.gsmDBM;
       dataInfo.relSignalStrength = message.gsmRelative;
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", dataInfo);
+      this._sendMobileConnectionMessage("RIL:DataInfoChanged", dataInfo);
     }
   },
 
   networkChanged: function networkChanged(srcNetwork, destNetwork) {
     return !destNetwork ||
       destNetwork.longName != srcNetwork.longName ||
       destNetwork.shortName != srcNetwork.shortName ||
       destNetwork.mnc != srcNetwork.mnc ||
@@ -782,22 +902,22 @@ RadioInterfaceLayer.prototype = {
   },
 
   handleOperatorChange: function handleOperatorChange(message) {
     let voice = this.rilContext.voice;
     let data = this.rilContext.data;
 
     if (this.networkChanged(message, voice.network)) {
       voice.network = message;
-      ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voice);
+      this._sendMobileConnectionMessage("RIL:VoiceInfoChanged", voice);
     }
 
     if (this.networkChanged(message, data.network)) {
       data.network = message;
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", data);
+      this._sendMobileConnectionMessage("RIL:DataInfoChanged", data);
     }
   },
 
   handleRadioStateChange: function handleRadioStateChange(message) {
     this._changingRadioPower = false;
 
     let newState = message.radioState;
     if (this.rilContext.radioState == newState) {
@@ -984,30 +1104,30 @@ RadioInterfaceLayer.prototype = {
     if (call.isActive) {
       this._activeCall = call;
     } else if (this._activeCall &&
                this._activeCall.callIndex == call.callIndex) {
       // Previously active call is not active now.
       this._activeCall = null;
     }
     this.updateCallAudioState();
-    ppmm.broadcastAsyncMessage("RIL:CallStateChanged", call);
+    this._sendTelephonyMessage("RIL:CallStateChanged", call);
   },
 
   /**
    * Handle call disconnects by updating our current state and the audio system.
    */
   handleCallDisconnected: function handleCallDisconnected(call) {
     debug("handleCallDisconnected: " + JSON.stringify(call));
     if (call.isActive) {
       this._activeCall = null;
     }
     this.updateCallAudioState();
     call.state = nsIRadioInterfaceLayer.CALL_STATE_DISCONNECTED;
-    ppmm.broadcastAsyncMessage("RIL:CallStateChanged", call);
+    this._sendTelephonyMessage("RIL:CallStateChanged", call);
   },
 
   /**
    * Handle calls delivered in response to a 'enumerateCalls' request.
    */
   handleEnumerateCalls: function handleEnumerateCalls(options) {
     debug("handleEnumerateCalls: " + JSON.stringify(options));
     for (let i in options.calls) {
@@ -1025,17 +1145,17 @@ RadioInterfaceLayer.prototype = {
     this._sendRequestResults("RIL:GetAvailableNetworks", message);
   },
 
   /**
    * Update network selection mode
    */
   updateNetworkSelectionMode: function updateNetworkSelectionMode(message) {
     debug("updateNetworkSelectionMode: " + JSON.stringify(message));
-    ppmm.broadcastAsyncMessage("RIL:NetworkSelectionModeChanged", message);
+    this._sendMobileConnectionMessage("RIL:NetworkSelectionModeChanged", message);
   },
 
   /**
    * Handle "manual" network selection request.
    */
   handleSelectNetwork: function handleSelectNetwork(message) {
     debug("handleSelectNetwork: " + JSON.stringify(message));
     this._sendRequestResults("RIL:SelectNetwork", message);
@@ -1048,17 +1168,17 @@ RadioInterfaceLayer.prototype = {
     debug("handleSelectNetworkAuto: " + JSON.stringify(message));
     this._sendRequestResults("RIL:SelectNetworkAuto", message);
   },
 
   /**
    * Handle call error.
    */
   handleCallError: function handleCallError(message) {
-    ppmm.broadcastAsyncMessage("RIL:CallError", message);   
+    this._sendTelephonyMessage("RIL:CallError", message);
   },
 
   /**
    * Handle WDP port push PDU. Constructor WDP bearer information and deliver
    * to WapPushManager.
    *
    * @param message
    *        A SMS message.
@@ -1109,17 +1229,17 @@ RadioInterfaceLayer.prototype = {
     // For now we don't store indicators persistently. When the mwi.discard
     // flag is false, we'll need to persist the indicator to EFmwis.
     // See TS 23.040 9.2.3.24.2
 
     let mwi = message.mwi;
     if (mwi) {
       mwi.returnNumber = message.sender || null;
       mwi.returnMessage = message.fullBody || null;
-      ppmm.broadcastAsyncMessage("RIL:VoicemailNotification", mwi);
+      this._sendVoicemailMessage("RIL:VoicemailNotification", mwi);
       return;
     }
 
     let id = -1;
     if (message.messageClass != RIL.PDU_DCS_MSG_CLASS_0) {
       id = gSmsDatabaseService.saveReceivedMessage(message.sender || null,
                                                    message.fullBody || null,
                                                    message.timestamp);
@@ -1225,17 +1345,17 @@ RadioInterfaceLayer.prototype = {
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     let data = this.rilContext.data;
 
     if (datacall.ifname &&
         datacall.apn == this.dataCallSettings["apn"]) {
       data.connected = (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED);
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", data);
+      this._sendMobileConnectionMessage("RIL:DataInfoChanged", data);
     }
 
     this._deliverDataCallCallback("dataCallStateChanged",
                                   [datacall]);
   },
 
   /**
    * Handle data call list.
@@ -1282,26 +1402,26 @@ RadioInterfaceLayer.prototype = {
                          oldIcc.iccid != message.iccid ||
                          oldIcc.mcc != message.mcc || 
                          oldIcc.mnc != message.mnc;
     if (!iccInfoChanged) {
       return;
     }
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when the MCC or MNC codes have changed.
-    ppmm.broadcastAsyncMessage("RIL:IccInfoChanged", message);
+    this._sendMobileConnectionMessage("RIL:IccInfoChanged", message);
   },
 
   handleICCCardLockResult: function handleICCCardLockResult(message) {
     this._sendRequestResults("RIL:CardLockResult", message);
   },
 
   handleUSSDReceived: function handleUSSDReceived(ussd) {
     debug("handleUSSDReceived " + JSON.stringify(ussd));
-    ppmm.broadcastAsyncMessage("RIL:USSDReceived", ussd);
+    this._sendMobileConnectionMessage("RIL:UssdReceived", ussd);
   },
 
   handleSendMMI: function handleSendMMI(message) {
     debug("handleSendMMI " + JSON.stringify(message));
     let messageType = message.success ? "RIL:SendMMI:Return:OK" :
                                         "RIL:SendMMI:Return:KO";
     this._sendRequestResults(messageType, message);
   },
@@ -1311,39 +1431,43 @@ RadioInterfaceLayer.prototype = {
     let messageType = message.success ? "RIL:CancelMMI:Return:OK" :
                                         "RIL:CancelMMI:Return:KO";
     this._sendRequestResults(messageType, message);
   },
 
   handleStkProactiveCommand: function handleStkProactiveCommand(message) {
     debug("handleStkProactiveCommand " + JSON.stringify(message));
     gSystemMessenger.broadcastMessage("icc-stkcommand", message);
-    ppmm.broadcastAsyncMessage("RIL:StkCommand", message);
+    this._sendMobileConnectionMessage("RIL:StkCommand", message);
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     switch (topic) {
       case kSysMsgListenerReadyObserverTopic:
         Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
         this._sysMsgListenerReady = true;
         this._ensureRadioState();
         break;
       case kMozSettingsChangedObserverTopic:
         let setting = JSON.parse(data);
         this.handle(setting.key, setting.value);
         break;
       case "xpcom-shutdown":
+        ppmm.removeMessageListener("child-process-shutdown", this);
         for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) {
           ppmm.removeMessageListener(msgname, this);
         }
         for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
           ppmm.removeMessageListener(msgname, this);
         }
+        for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
+          ppmm.removeMessageListener(msgname, this);
+        }
         // Shutdown all RIL network interfaces
         this.dataNetworkInterface.shutdown();
         this.mmsNetworkInterface.shutdown();
         this.suplNetworkInterface.shutdown();
         ppmm = null;
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
         break;
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -134,26 +134,44 @@ interface nsIRILContactCallback : nsISup
                            in DOMString contactType,
                            in jsval contacts);
 };
 
 /**
  * Helper that runs in the content process and exposes information
  * to the DOM.
  */
-[scriptable, uuid(31ea634b-e710-49ce-bb7d-bfcbaa40fb00)]
+[scriptable, uuid(2abe554b-e07c-44b0-9719-02190460188d)]
 interface nsIRILContentHelper : nsIMobileConnectionProvider
 {
   void registerTelephonyCallback(in nsIRILTelephonyCallback callback);
   void unregisterTelephonyCallback(in nsIRILTelephonyCallback callback);
 
   void registerVoicemailCallback(in nsIRILVoicemailCallback callback);
   void unregisterVoicemailCallback(in nsIRILVoicemailCallback callback);
 
   /**
+   * Called when a content process registers receiving unsolicited messages from
+   * RadioInterfaceLayer in the chrome process. Only content granted telephony
+   * permission is allowed to register. Note that a content doesn't need to
+   * unregister because the chrome process will remove it from the registration
+   * list once the chrome receives a 'child-process-shutdown' message.
+   */
+   void registerTelephonyMsg();
+
+  /**
+   * Called when a content process registers receiving unsolicited messages from
+   * RadioInterfaceLayer in the chrome process. Only a content granted voice
+   * mail permission is allowed to register. Note that a content doesn't need to
+   * unregister because the chrome process will remove it from the registration
+   * list once the chrome receives a 'child-process-shutdown' message.
+   */
+   void registerVoicemailMsg();
+
+  /**
    * Will continue calling callback.enumerateCallState until the callback
    * returns false.
    */
   void enumerateCalls(in nsIRILTelephonyCallback callback);
 
   /**
    * Functionality for making and managing phone calls.
    */
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -89,16 +89,19 @@ Telephony::Create(nsPIDOMWindow* aOwner,
   telephony->mRILTelephonyCallback = new RILTelephonyCallback(telephony);
 
   nsresult rv = aRIL->EnumerateCalls(telephony->mRILTelephonyCallback);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = aRIL->RegisterTelephonyCallback(telephony->mRILTelephonyCallback);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
+  rv = aRIL->RegisterTelephonyMsg();
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
   return telephony.forget();
 }
 
 already_AddRefed<TelephonyCall>
 Telephony::CreateNewDialingCall(const nsAString& aNumber)
 {
   nsRefPtr<TelephonyCall> call =
     TelephonyCall::Create(this, aNumber,
--- a/dom/telephony/Voicemail.cpp
+++ b/dom/telephony/Voicemail.cpp
@@ -45,16 +45,21 @@ Voicemail::Voicemail(nsPIDOMWindow* aWin
   BindToOwner(aWindow);
 
   mRILVoicemailCallback = new RILVoicemailCallback(this);
 
   nsresult rv = aRIL->RegisterVoicemailCallback(mRILVoicemailCallback);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed registering voicemail callback with RIL");
   }
+
+  rv = aRIL->RegisterVoicemailMsg();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed registering voicemail messages with RIL");
+  }
 }
 
 Voicemail::~Voicemail()
 {
   if (mRIL && mRILVoicemailCallback) {
     mRIL->UnregisterVoicemailCallback(mRILVoicemailCallback);
   }
 }