Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS (part 5-2, MMS APN settings). r=vicamo
authorGene Lian <clian@mozilla.com>
Sat, 02 Nov 2013 18:17:58 +0800
changeset 153392 8aff9442f3e1685ba39aab704316a2e50b1f41ff
parent 153391 781a9cd66ba5eddfbb27993e868bb8b0a0673896
child 153393 7e1e62c5b0ff7ef6dd2a5fa878f5f8f7a2cae267
push id35795
push userryanvm@gmail.com
push dateMon, 04 Nov 2013 21:31:44 +0000
treeherdermozilla-inbound@076fb6b1d2f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvicamo
bugs854326
milestone28.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 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS (part 5-2, MMS APN settings). r=vicamo
dom/mobilemessage/src/gonk/MmsService.js
dom/system/gonk/NetworkManager.js
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/nsIRadioInterfaceLayer.idl
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -151,28 +151,50 @@ function MmsConnection(aServiceId) {
   this.radioInterface = ril.getRadioInterface(aServiceId);
 };
 
 MmsConnection.prototype = {
     // To Vicamo: for your convenience of reviewing, I will align the following codes before landing.
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
     /** MMS proxy settings. */
-    mmsc: null,
-    proxy: null,
-    port: null,
+    mmsc:     "",
+    mmsProxy: "",
+    mmsPort:  -1,
+
+    setApnSetting: function setApnSetting(network) {
+      this.mmsc = network.mmsc;
+      this.mmsProxy = network.mmsProxy;
+      this.mmsPort = network.mmsPort;
+    },
+
+    get proxyInfo() {
+      if (!this.mmsProxy) {
+        if (DEBUG) debug("getProxyInfo: MMS proxy is not available.");
+        return null;
+      }
+
+      let port = this.mmsPort;
+      if (port == -1) {
+        port = 80;
+        if (DEBUG) debug("getProxyInfo: port is not valid. Set to defult (80).");
+      }
+
+      let proxyInfo =
+        gpps.newProxyInfo("http", this.mmsProxy, port,
+                          Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST,
+                          -1, null);
+      if (DEBUG) debug("getProxyInfo: " + JSON.stringify(proxyInfo));
+
+      return proxyInfo;
+    },
 
     // For keeping track of the radio status.
     radioDisabled: false,
-
-    proxyInfo: null,
-    settings: [kPrefRilMmsc,
-               kPrefRilMmsProxy,
-               kPrefRilMmsPort,
-               kPrefRilRadioDisabled],
+    settings: ["ril.radio.disabled"],
     connected: false,
 
     //A queue to buffer the MMS HTTP requests when the MMS network
     //is not yet connected. The buffered requests will be cleared
     //if the MMS network fails to be connected within a timer.
     pendingCallbacks: [],
 
     /** MMS network connection reference count. */
@@ -209,28 +231,16 @@ MmsConnection.prototype = {
       Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic,
                                false);
       Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
       this.settings.forEach(function(name) {
         Services.prefs.addObserver(name, this, false);
       }, this);
 
       try {
-        this.mmsc = Services.prefs.getCharPref(kPrefRilMmsc);
-        this.proxy = Services.prefs.getCharPref(kPrefRilMmsProxy);
-        this.port = Services.prefs.getIntPref(kPrefRilMmsPort);
-        this.updateProxyInfo();
-      } catch (e) {
-        if (DEBUG) debug("Unable to initialize the MMS proxy settings from " +
-                         "the preference. This could happen at the first-run. " +
-                         "Should be available later.");
-        this.clearMmsProxySettings();
-      }
-
-      try {
         this.radioDisabled = Services.prefs.getBoolPref(kPrefRilRadioDisabled);
       } catch (e) {
         if (DEBUG) debug("Getting preference 'ril.radio.disabled' fails.");
         this.radioDisabled = false;
       }
 
       this.connected = this.radioInterface.getDataCallStateByType("mms") ==
         Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
@@ -296,18 +306,19 @@ MmsConnection.prototype = {
       return iccId;
     },
 
     /**
      * Acquire the MMS network connection.
      *
      * @param callback
      *        Callback function when either the connection setup is done,
-     *        timeout, or failed. Accepts a boolean value that indicates
-     *        whether the connection is ready.
+     *        timeout, or failed. Parameters are:
+     *        - A boolean value indicates whether the connection is ready.
+     *        - Acquire connection status: _HTTP_STATUS_ACQUIRE_*.
      *
      * @return true if the callback for MMS network connection is done; false
      *         otherwise.
      */
     acquire: function acquire(callback) {
       this.refCount++;
       this.connectTimer.cancel();
       this.disconnectTimer.cancel();
@@ -364,47 +375,16 @@ MmsConnection.prototype = {
         // since the MMS requests often come consecutively in a short time.
         this.disconnectTimer.
           initWithCallback(this.onDisconnectTimerTimeout.bind(this),
                            PREF_TIME_TO_RELEASE_MMS_CONNECTION,
                            Ci.nsITimer.TYPE_ONE_SHOT);
       }
     },
 
-    /**
-     * Update the MMS proxy info.
-     */
-    updateProxyInfo: function updateProxyInfo() {
-      if (this.proxy === null || this.port === null) {
-        if (DEBUG) debug("updateProxyInfo: proxy or port is not yet decided." );
-        return;
-      }
-
-      if (!this.port) {
-        this.port = 80;
-        if (DEBUG) debug("updateProxyInfo: port is 0. Set to defult port 80.");
-      }
-
-      this.proxyInfo =
-        gpps.newProxyInfo("http", this.proxy, this.port,
-                          Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST,
-                          -1, null);
-      if (DEBUG) debug("updateProxyInfo: " + JSON.stringify(this.proxyInfo));
-    },
-
-    /**
-     * Clear the MMS proxy settings.
-     */
-    clearMmsProxySettings: function clearMmsProxySettings() {
-      this.mmsc = null;
-      this.proxy = null;
-      this.port = null;
-      this.proxyInfo = null;
-    },
-
     shutdown: function shutdown() {
       Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
       Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
 
       this.connectTimer.cancel();
       this.flushPendingCallbacks(_HTTP_STATUS_RADIO_DISABLED);
       this.disconnectTimer.cancel();
       this.onDisconnectTimerTimeout();
@@ -429,54 +409,36 @@ MmsConnection.prototype = {
           this.connected =
             this.radioInterface.getDataCallStateByType("mms") ==
               Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
 
           if (!this.connected) {
             return;
           }
 
+          // Set up the MMS APN setting based on the network, which is going to
+          // be used for the HTTP requests later.
+          this.setApnSetting(network);
+
           if (DEBUG) debug("Got the MMS network connected! Resend the buffered " +
                            "MMS requests: number: " + this.pendingCallbacks.length);
           this.connectTimer.cancel();
           this.flushPendingCallbacks(_HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS)
           break;
         }
         case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: {
           if (data == kPrefRilRadioDisabled) {
             try {
               this.radioDisabled = Services.prefs.getBoolPref(kPrefRilRadioDisabled);
             } catch (e) {
               if (DEBUG) debug("Updating preference 'ril.radio.disabled' fails.");
               this.radioDisabled = false;
             }
             return;
           }
-
-          try {
-            switch (data) {
-              case kPrefRilMmsc:
-                this.mmsc = Services.prefs.getCharPref(kPrefRilMmsc);
-                break;
-              case kPrefRilMmsProxy:
-                this.proxy = Services.prefs.getCharPref(kPrefRilMmsProxy);
-                this.updateProxyInfo();
-                break;
-              case kPrefRilMmsPort:
-                this.port = Services.prefs.getIntPref(kPrefRilMmsPort);
-                this.updateProxyInfo();
-                break;
-              default:
-                break;
-            }
-          } catch (e) {
-            if (DEBUG) debug("Failed to update the MMS proxy settings from the" +
-                             "preference.");
-            this.clearMmsProxySettings();
-          }
           break;
         }
         case NS_XPCOM_SHUTDOWN_OBSERVER_ID: {
           this.shutdown();
         }
       }
     }
 };
@@ -536,17 +498,17 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
     /**
      * Send MMS request to MMSC.
      *
      * @param mmsConnection
      *        The MMS connection.
      * @param method
      *        "GET" or "POST".
      * @param url
-     *        Target url string.
+     *        Target url string or null to be replaced by mmsc url.
      * @param istream
      *        An nsIInputStream instance as data source to be sent or null.
      * @param callback
      *        A callback function that takes two arguments: one for http
      *        status, the other for wrapped PDU data for further parsing.
      */
     sendRequest: function sendRequest(mmsConnection, method, url, istream,
                                       callback) {
@@ -599,16 +561,21 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
 
           if (!cancellable.isDone) {
             cancellable.done(cancellable.isCancelled ?
                              _HTTP_STATUS_USER_CANCELLED : errorCode, null);
           }
           return;
         }
 
+        // MMSC is available after an MMS connection is successfully acquired.
+        if (!url) {
+          url = mmsConnection.mmsc;
+        }
+
         if (DEBUG) debug("sendRequest: register proxy filter to " + url);
         let proxyFilter = new MmsProxyFilter(mmsConnection, url);
         gpps.registerFilter(proxyFilter, 0);
 
         cancellable.xhr = this.sendHttpRequest(mmsConnection, method,
                                                url, istream, proxyFilter,
                                                cancellable.done.bind(cancellable));
       }).bind(this));
@@ -761,19 +728,20 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
           if (name && name.length > MMS.MMS_MAX_LENGTH_NAME_CONTENT_TYPE) {
             return false;
           }
         }
       }
       return true;
     },
 
-    translateHttpStatusToMmsStatus: function translateHttpStatusToMmsStatus(httpStatus,
-                                                                            cancelledReason,
-                                                                            defaultStatus) {
+    translateHttpStatusToMmsStatus:
+      function translateHttpStatusToMmsStatus(httpStatus,
+                                              cancelledReason,
+                                              defaultStatus) {
       switch(httpStatus) {
         case _HTTP_STATUS_USER_CANCELLED:
           return cancelledReason;
         case _HTTP_STATUS_RADIO_DISABLED:
           return _MMS_ERROR_RADIO_DISABLED;
         case _HTTP_STATUS_NO_SIM_CARD:
           return _MMS_ERROR_NO_SIM_CARD;
         case HTTP_STATUS_OK:
@@ -828,17 +796,17 @@ NotifyResponseTransaction.prototype = {
         // `The MMS Client SHOULD ignore the associated HTTP POST response
         // from the MMS Proxy-Relay.` ~ OMA-TS-MMS_CTR-V1_3-20110913-A
         // section 8.2.2 "Notification".
         callback(httpStatus);
       };
     }
     gMmsTransactionHelper.sendRequest(this.mmsConnection,
                                       "POST",
-                                      this.mmsConnection.mmsc,
+                                      null,
                                       this.istream,
                                       requestCallback);
   }
 };
 
 /**
  * CancellableTransaction - base class inherited by [Send|Retrieve]Transaction.
  * @param cancellableId
@@ -1248,21 +1216,22 @@ SendTransaction.prototype = Object.creat
    */
   send: {
     value: function send(callback) {
       this.timer = null;
 
       this.cancellable =
         gMmsTransactionHelper.sendRequest(this.mmsConnection,
                                           "POST",
-                                          this.mmsConnection.mmsc,
+                                          null,
                                           this.istream,
                                           (function (httpStatus, data) {
         let mmsStatus = gMmsTransactionHelper.
-                          translateHttpStatusToMmsStatus(httpStatus,
+                          translateHttpStatusToMmsStatus(
+                            httpStatus,
                             this.cancelledReason,
                             MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE);
         if (httpStatus != HTTP_STATUS_OK) {
           callback(mmsStatus, null);
           return;
         }
 
         if (!data) {
@@ -1323,17 +1292,17 @@ AcknowledgeTransaction.prototype = {
         // `The MMS Client SHOULD ignore the associated HTTP POST response
         // from the MMS Proxy-Relay.` ~ OMA-TS-MMS_CTR-V1_3-20110913-A
         // section 8.2.3 "Retrieving an MM".
         callback(httpStatus);
       };
     }
     gMmsTransactionHelper.sendRequest(this.mmsConnection,
                                       "POST",
-                                      this.mmsConnection.mmsc,
+                                      null,
                                       this.istream,
                                       requestCallback);
   }
 };
 
 function getDefaultServiceId() {
   let id = Services.prefs.getIntPref(kPrefDefaultServiceId);
   let numRil = Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -161,20 +161,24 @@ function NetworkManager() {
   }
   Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
 
   // Possible usb tethering interfaces for different gonk platform.
   this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(",");
 
   // Default values for internal and external interfaces.
   this._tetheringInterface = Object.create(null);
-  this._tetheringInterface[TETHERING_TYPE_USB] = {externalInterface: DEFAULT_3G_INTERFACE_NAME,
-                                                  internalInterface: DEFAULT_USB_INTERFACE_NAME};
-  this._tetheringInterface[TETHERING_TYPE_WIFI] = {externalInterface: DEFAULT_3G_INTERFACE_NAME,
-                                                   internalInterface: DEFAULT_WIFI_INTERFACE_NAME};
+  this._tetheringInterface[TETHERING_TYPE_USB] = {
+    externalInterface: DEFAULT_3G_INTERFACE_NAME,
+    internalInterface: DEFAULT_USB_INTERFACE_NAME
+  };
+  this._tetheringInterface[TETHERING_TYPE_WIFI] = {
+    externalInterface: DEFAULT_3G_INTERFACE_NAME,
+    internalInterface: DEFAULT_WIFI_INTERFACE_NAME
+  };
 
   this.initTetheringSettings();
 
   let settingsLock = gSettingsService.createLock();
   // Read usb tethering data from settings DB.
   settingsLock.get(SETTINGS_USB_IP, this);
   settingsLock.get(SETTINGS_USB_PREFIX, this);
   settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
@@ -244,26 +248,27 @@ NetworkManager.prototype = {
 #endif
             // Remove pre-created default route and let setAndConfigureActive()
             // to set default route only on preferred network
             this.removeDefaultRoute(network.name);
             this.setAndConfigureActive();
 #ifdef MOZ_B2G_RIL
             // Update data connection when Wifi connected/disconnected
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-              for (let i = 0; i < this.mRIL.numRadioInterfaces; i++) {
-                this.mRIL.getRadioInterface(i).updateRILNetworkInterface();
+              for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
+                this.mRil.getRadioInterface(i).updateRILNetworkInterface();
               }
             }
 #endif
 
             this.onConnectionChanged(network);
 
             // Probing the public network accessibility after routing table is ready
-            CaptivePortalDetectionHelper.notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
+            CaptivePortalDetectionHelper
+              .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
             break;
           case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
 #ifdef MOZ_B2G_RIL
             // Remove host route for data calls
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE ||
                 network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS ||
                 network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL) {
               this.removeHostRoute(network);
@@ -274,24 +279,26 @@ NetworkManager.prototype = {
             // Remove routing table in /proc/net/route
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
               this.resetRoutingTable(network);
 #ifdef MOZ_B2G_RIL
             } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
               this.removeDefaultRoute(network.name);
 #endif
             }
+
             // Abort ongoing captive portal detection on the wifi interface
-            CaptivePortalDetectionHelper.notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
+            CaptivePortalDetectionHelper
+              .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
             this.setAndConfigureActive();
 #ifdef MOZ_B2G_RIL
             // Update data connection when Wifi connected/disconnected
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-              for (let i = 0; i < this.mRIL.numRadioInterfaces; i++) {
-                this.mRIL.getRadioInterface(i).updateRILNetworkInterface();
+              for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
+                this.mRil.getRadioInterface(i).updateRILNetworkInterface();
               }
             }
 #endif
             break;
         }
         break;
 #ifdef MOZ_B2G_RIL
       case TOPIC_INTERFACE_REGISTERED:
@@ -514,30 +521,54 @@ NetworkManager.prototype = {
       callback.call(this, response);
       delete this.controlCallbacks[id];
     }
   },
 
 #ifdef MOZ_B2G_RIL
   setExtraHostRoute: function setExtraHostRoute(network) {
     if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
-      debug("Network '" + network.name + "' registered, adding mmsproxy and/or mmsc route");
-      let mmsHosts = this.resolveHostname(
-                       [Services.prefs.getCharPref("ril.mms.mmsproxy"),
-                        Services.prefs.getCharPref("ril.mms.mmsc")]);
+      if (!(network instanceof Ci.nsIRilNetworkInterface)) {
+        debug("Network for MMS must be an instance of nsIRilNetworkInterface");
+        return;
+      }
+
+      network = network.QueryInterface(Ci.nsIRilNetworkInterface);
+
+      debug("Network '" + network.name + "' registered, " +
+            "adding mmsproxy and/or mmsc route");
+
+      let mmsHosts = this.resolveHostname([network.mmsProxy, network.mmsc]);
+      if (mmsHosts.length == 0) {
+        debug("No valid hostnames can be added. Stop adding host route.");
+        return;
+      }
+
       this.addHostRouteWithResolve(network, mmsHosts);
     }
   },
 
   removeExtraHostRoute: function removeExtraHostRoute(network) {
     if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
-      debug("Network '" + network.name + "' unregistered, removing mmsproxy and/or mmsc route");
-      let mmsHosts = this.resolveHostname(
-                       [Services.prefs.getCharPref("ril.mms.mmsproxy"),
-                        Services.prefs.getCharPref("ril.mms.mmsc")]);
+      if (!(network instanceof Ci.nsIRilNetworkInterface)) {
+        debug("Network for MMS must be an instance of nsIRilNetworkInterface");
+        return;
+      }
+
+      network = network.QueryInterface(Ci.nsIRilNetworkInterface);
+
+      debug("Network '" + network.name + "' unregistered, " +
+            "removing mmsproxy and/or mmsc route");
+
+      let mmsHosts = this.resolveHostname([network.mmsProxy, network.mmsc]);
+      if (mmsHosts.length == 0) {
+        debug("No valid hostnames can be removed. Stop removing host route.");
+        return;
+      }
+
       this.removeHostRouteWithResolve(network, mmsHosts);
     }
   },
 #endif // MOZ_B2G_RIL
 
   /**
    * Determine the active interface and configure it.
    */
@@ -700,21 +731,28 @@ NetworkManager.prototype = {
     };
     this.worker.postMessage(options);
   },
 
   resolveHostname: function resolveHostname(hosts) {
     let retval = [];
 
     for (let hostname of hosts) {
+      // Sanity check for null, undefined and empty string... etc.
+      if (!hostname) {
+        continue;
+      }
+
       try {
         let uri = Services.io.newURI(hostname, null, null);
         hostname = uri.host;
       } catch (e) {}
 
+      // An extra check for hostnames that cannot be made by newURI(...).
+      // For example, an IP address like "10.1.1.1".
       if (hostname.match(this.REGEXP_IPV4) ||
           hostname.match(this.REGEXP_IPV6)) {
         retval.push(hostname);
         continue;
       }
 
       try {
         let hostnameIps = gDNSService.resolve(hostname, 0);
@@ -1141,17 +1179,18 @@ NetworkManager.prototype = {
   onConnectionChangedReport: function onConnectionChangedReport(data) {
     let code = data.resultCode;
     let reason = data.resultReason;
 
     debug("onConnectionChangedReport result: Code " + code + " reason " + reason);
 
     if (!isError(code)) {
       // Update the external interface.
-      this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = data.current.externalIfname;
+      this._tetheringInterface[TETHERING_TYPE_USB]
+          .externalInterface = data.current.externalIfname;
       debug("Change the interface name to " + data.current.externalIfname);
     }
   },
 
   onConnectionChanged: function onConnectionChanged(network) {
     if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
       debug("We are only interested in CONNECTED event");
       return;
@@ -1197,17 +1236,18 @@ NetworkManager.prototype = {
 
 let CaptivePortalDetectionHelper = (function() {
 
   const EVENT_CONNECT = "Connect";
   const EVENT_DISCONNECT = "Disconnect";
   let _ongoingInterface = null;
   let _available = ("nsICaptivePortalDetector" in Ci);
   let getService = function () {
-    return Cc['@mozilla.org/toolkit/captive-detector;1'].getService(Ci.nsICaptivePortalDetector);
+    return Cc['@mozilla.org/toolkit/captive-detector;1']
+             .getService(Ci.nsICaptivePortalDetector);
   };
 
   let _performDetection = function (interfaceName, callback) {
     let capService = getService();
     let capCallback = {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalCallback]),
       prepare: function prepare() {
         capService.finishPreparation(interfaceName);
@@ -1264,17 +1304,17 @@ let CaptivePortalDetectionHelper = (func
           }
           break;
       }
     }
   };
 }());
 
 #ifdef MOZ_B2G_RIL
-XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRIL",
+XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRil",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 #endif
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkManager]);
 
 
 let debug;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -3319,18 +3319,16 @@ RILNetworkInterface.prototype = {
   // nsINetworkInterface
 
   NETWORK_STATE_UNKNOWN:       Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
   NETWORK_STATE_CONNECTING:    Ci.nsINetworkInterface.CONNECTING,
   NETWORK_STATE_CONNECTED:     Ci.nsINetworkInterface.CONNECTED,
   NETWORK_STATE_DISCONNECTING: Ci.nsINetworkInterface.DISCONNECTING,
   NETWORK_STATE_DISCONNECTED:  Ci.nsINetworkInterface.DISCONNECTED,
 
-  state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
-
   NETWORK_TYPE_WIFI:        Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
   NETWORK_TYPE_MOBILE:      Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
   NETWORK_TYPE_MOBILE_MMS:  Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS,
   NETWORK_TYPE_MOBILE_SUPL: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
   // The network manager should only need to add the host route for "other"
   // types, which is the same handling method as the supl type. So let the
   // definition of other types to be the same as the one of supl type.
   NETWORK_TYPE_MOBILE_OTHERS: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
@@ -3341,16 +3339,22 @@ RILNetworkInterface.prototype = {
    */
   NETWORK_APNRETRY_FACTOR: 8,
   NETWORK_APNRETRY_ORIGIN: 3,
   NETWORK_APNRETRY_MAXRETRIES: 10,
 
   // Event timer for connection retries
   timer: null,
 
+  /**
+   * nsINetworkInterface Implementation
+   */
+
+  state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
+
   get type() {
     if (this.connectedTypes.indexOf("default") != -1) {
       return this.NETWORK_TYPE_MOBILE;
     }
     if (this.connectedTypes.indexOf("mms") != -1) {
       return this.NETWORK_TYPE_MOBILE_MMS;
     }
     if (this.connectedTypes.indexOf("supl") != -1) {
@@ -3374,25 +3378,83 @@ RILNetworkInterface.prototype = {
   get httpProxyHost() {
     return this.apnSetting.proxy || '';
   },
 
   get httpProxyPort() {
     return this.apnSetting.port || '';
   },
 
+  /**
+   * nsIRilNetworkInterface Implementation
+   */
+
   get serviceId() {
     return this.radioInterface.clientId;
   },
 
   get iccId() {
     let iccInfo = this.radioInterface.rilContext.iccInfo;
     return iccInfo && iccInfo.iccid;
   },
 
+  get mmsc() {
+    if (!this.inConnectedTypes("mms")) {
+      if (DEBUG) this.debug("Error! Only MMS network can get MMSC.");
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    let mmsc = this.apnSetting.mmsc;
+    if (!mmsc) {
+      try {
+        mmsc = Services.prefs.getCharPref("ril.mms.mmsc");
+      } catch (e) {
+        mmsc = "";
+      }
+    }
+
+    return mmsc;
+  },
+
+  get mmsProxy() {
+    if (!this.inConnectedTypes("mms")) {
+      if (DEBUG) this.debug("Error! Only MMS network can get MMS proxy.");
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    let proxy = this.apnSetting.mmsproxy;
+    if (!proxy) {
+      try {
+        proxy = Services.prefs.getCharPref("ril.mms.mmsproxy");
+      } catch (e) {
+        proxy = "";
+      }
+    }
+
+    return proxy;
+  },
+
+  get mmsPort() {
+    if (!this.inConnectedTypes("mms")) {
+      if (DEBUG) this.debug("Error! Only MMS network can get MMS port.");
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    let port = this.apnSetting.mmsport;
+    if (!port) {
+      try {
+        port = Services.prefs.getIntPref("ril.mms.mmsport");
+      } catch (e) {
+        port = -1;
+      }
+    }
+
+    return port;
+  },
+
   debug: function debug(s) {
     dump("-*- RILNetworkInterface[" + this.radioInterface.clientId + ":" +
          this.type + "]: " + s + "\n");
   },
 
   // nsIRILDataCallback
 
   dataCallError: function dataCallError(message) {
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -4,21 +4,26 @@
 
 #include "nsISupports.idl"
 #include "nsINetworkManager.idl"
 
 interface nsIDOMMozIccInfo;
 interface nsIDOMMozMobileConnectionInfo;
 interface nsIMobileMessageCallback;
 
-[scriptable, uuid(a15769f1-538e-4b4b-81da-c52866d38f88)]
+[scriptable, uuid(6e0f45b8-410e-11e3-8c8e-b715b2cd0128)]
 interface nsIRilNetworkInterface : nsINetworkInterface
 {
   readonly attribute unsigned long serviceId;
   readonly attribute DOMString iccId;
+
+  /* The following attributes are for MMS proxy settings. */
+  readonly attribute DOMString mmsc;     // Empty string if not set.
+  readonly attribute DOMString mmsProxy; // Empty string if not set.
+  readonly attribute long      mmsPort;  // -1 if not set.
 };
 
 [scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)]
 interface nsIRILDataCallInfo : nsISupports
 {
   /**
    * Current data call state, one of the
    * nsINetworkInterface::NETWORK_STATE_* constants.