Bug 1032097 - Part 6: Adopt addHostRoute/removeHostRoute in MmsService. r=vyang
authorBevis Tseng <btseng@mozilla.com>
Mon, 11 Aug 2014 17:41:28 +0800
changeset 201159 d9e5a211e546be30443fd8ea871aa377d92740d8
parent 201158 4bdcfc5817d3f4544f7513b590cd37f0b9446511
child 201160 9e5ad094909069359baaad145168f92b6ac5755a
push id48102
push userkwierso@gmail.com
push dateSat, 23 Aug 2014 00:05:41 +0000
treeherdermozilla-inbound@09ee525ce99d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvyang
bugs1032097
milestone34.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 1032097 - Part 6: Adopt addHostRoute/removeHostRoute in MmsService. r=vyang
dom/mobilemessage/src/gonk/MmsService.js
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -5,19 +5,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-
-Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 const RIL_MMSSERVICE_CONTRACTID = "@mozilla.org/mms/rilmmsservice;1";
 const RIL_MMSSERVICE_CID = Components.ID("{217ddd76-75db-4210-955d-8806cd8d87f9}");
 
 let DEBUG = false;
 function debug(s) {
   dump("-@- MmsService: " + s + "\n");
 };
@@ -49,25 +48,27 @@ const kPrefRilRadioDisabled             
 const HTTP_STATUS_OK = 200;
 
 // Non-standard HTTP status for internal use.
 const _HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS  =  0;
 const _HTTP_STATUS_USER_CANCELLED              = -1;
 const _HTTP_STATUS_RADIO_DISABLED              = -2;
 const _HTTP_STATUS_NO_SIM_CARD                 = -3;
 const _HTTP_STATUS_ACQUIRE_TIMEOUT             = -4;
+const _HTTP_STATUS_FAILED_TO_ROUTE             = -5;
 
 // Non-standard MMS status for internal use.
 const _MMS_ERROR_MESSAGE_DELETED               = -1;
 const _MMS_ERROR_RADIO_DISABLED                = -2;
 const _MMS_ERROR_NO_SIM_CARD                   = -3;
 const _MMS_ERROR_SIM_CARD_CHANGED              = -4;
 const _MMS_ERROR_SHUTDOWN                      = -5;
 const _MMS_ERROR_USER_CANCELLED_NO_REASON      = -6;
 const _MMS_ERROR_SIM_NOT_MATCHED               = -7;
+const _MMS_ERROR_FAILED_TO_ROUTE               = -8;
 
 const CONFIG_SEND_REPORT_NEVER       = 0;
 const CONFIG_SEND_REPORT_DEFAULT_NO  = 1;
 const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
 const CONFIG_SEND_REPORT_ALWAYS      = 3;
 
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
 
@@ -153,16 +154,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRil",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
+                                   "@mozilla.org/network/manager;1",
+                                   "nsINetworkManager");
+
 XPCOMUtils.defineLazyGetter(this, "MMS", function() {
   let MMS = {};
   Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
   return MMS;
 });
 
 // Internal Utilities
 
@@ -197,31 +202,33 @@ function getRadioDisabledState() {
 
 /**
  * Helper Class to control MMS Data Connection.
  */
 function MmsConnection(aServiceId) {
   this.serviceId = aServiceId;
   this.radioInterface = gRil.getRadioInterface(aServiceId);
   this.pendingCallbacks = [];
+  this.hostsToRoute = [];
   this.connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   this.disconnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 };
 
 MmsConnection.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   /** MMS proxy settings. */
   mmsc:     "",
   mmsProxy: "",
   mmsPort:  -1,
 
   setApnSetting: function(network) {
     this.mmsc = network.mmsc;
-    this.mmsProxy = network.mmsProxy;
+    // Workaround an xpconnect issue with undefined string objects. See bug 808220.
+    this.mmsProxy = (network === "undefined") ? undefined : network.mmsProxy;
     this.mmsPort = network.mmsPort;
   },
 
   get proxyInfo() {
     if (!this.mmsProxy) {
       if (DEBUG) debug("getProxyInfo: MMS proxy is not available.");
       return null;
     }
@@ -247,16 +254,22 @@ MmsConnection.prototype = {
   //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: null,
 
   /** MMS network connection reference count. */
   refCount: 0,
 
+  // cache of hosts to be accessed when this connection is alive.
+  hostsToRoute: null,
+
+  // cache of the networkInterface acquired during this connection.
+  networkInterface: null,
+
   connectTimer: null,
 
   disconnectTimer: null,
 
   /**
    * Callback when |connectTimer| is timeout or cancelled by shutdown.
    */
   flushPendingCallbacks: function(status) {
@@ -269,19 +282,39 @@ MmsConnection.prototype = {
     }
   },
 
   /**
    * Callback when |disconnectTimer| is timeout or cancelled by shutdown.
    */
   onDisconnectTimerTimeout: function() {
     if (DEBUG) debug("onDisconnectTimerTimeout: deactivate the MMS data call.");
-    if (this.connected) {
+
+    if (!this.connected) {
+      return;
+    }
+
+    let deactivateMmsDataCall = (aError) => {
+      if (aError) debug("Failed to removeHostRoute: " + aError);
+
+      // Clear cache.
+      this.hostsToRoute = [];
+      this.networkInterface = null;
+
       this.radioInterface.deactivateDataCallByType("mms");
-    }
+    };
+
+    let promises =
+      this.hostsToRoute.map(function(aHost) {
+        return gNetworkManager.removeHostRoute(this.networkInterface, aHost);
+      }, this);
+
+    return Promise.all(promises)
+      .then(() => deactivateMmsDataCall(),
+            (aError) => deactivateMmsDataCall(aError));
   },
 
   init: function() {
     Services.obs.addObserver(this, kNetworkConnStateChangedTopic,
                              false);
     Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   },
 
@@ -388,16 +421,20 @@ MmsConnection.prototype = {
       if (errorStatus != null) {
         this.flushPendingCallbacks(errorStatus);
         return true;
       }
 
       if (DEBUG) debug("acquire: buffer the MMS request and setup the MMS data call.");
       this.radioInterface.setupDataCallByType("mms");
 
+      // Clear cache when setup new connection.
+      this.hostsToRoute = [];
+      this.networkInterface = null;
+
       // Set a timer to clear the buffered MMS requests if the
       // MMS network fails to be connected within a time period.
       this.connectTimer.
         initWithCallback(this.flushPendingCallbacks.bind(this, _HTTP_STATUS_ACQUIRE_TIMEOUT),
                          TIME_TO_BUFFER_MMS_REQUESTS,
                          Ci.nsITimer.TYPE_ONE_SHOT);
       return false;
     }
@@ -424,16 +461,44 @@ 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);
     }
   },
 
+  /**
+   * Helper to ensure the routing of each transaction.
+   *
+   * @param url
+   *        Optional url for retrieving mms.
+   *
+   * @return a Promise resolved if added or rejected, otherwise.
+   */
+  ensureRouting: function(url) {
+    let host = this.mmsProxy;
+
+    if (!this.mmsProxy) {
+      host = url;
+    }
+
+    try {
+      let uri = Services.io.newURI(host, null, null);
+      host = uri.host;
+    } catch (e) {}
+
+    return gNetworkManager.addHostRoute(this.networkInterface, host)
+      .then(() => {
+        if (this.hostsToRoute.indexOf(host) < 0) {
+          this.hostsToRoute.push(host);
+        }
+      });
+  },
+
   shutdown: function() {
     Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
 
     this.connectTimer.cancel();
     this.flushPendingCallbacks(_HTTP_STATUS_RADIO_DISABLED);
     this.disconnectTimer.cancel();
     this.onDisconnectTimerTimeout();
@@ -462,27 +527,32 @@ MmsConnection.prototype = {
         // Return if the MMS network state doesn't change, where the network
         // state change can come from other non-MMS networks.
         if (connected == this.connected) {
           return;
         }
 
         this.connected = connected;
         if (!this.connected) {
+          this.hostsToRoute = [];
+          this.networkInterface = null;
           return;
         }
 
         // Set up the MMS APN setting based on the connected MMS network,
         // which is going to be used for the HTTP requests later.
         this.setApnSetting(network);
 
+        // Cache connected network.
+        this.networkInterface = 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)
+        this.flushPendingCallbacks(_HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS);
         break;
       }
       case NS_XPCOM_SHUTDOWN_OBSERVER_ID: {
         this.shutdown();
       }
     }
   }
 };
@@ -641,23 +711,33 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
           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);
+        let startTransaction = function () {
+          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));
+          cancellable.xhr = this.sendHttpRequest(mmsConnection, method,
+                                                 url, istream, proxyFilter,
+                                                 cancellable.done.bind(cancellable));
+        }.bind(this);
+
+        mmsConnection.ensureRouting(url)
+          .then(() => startTransaction(),
+                (aError) => {
+                  debug("Failed to ensureRouting: " + aError);
+
+                  cancellable.done(_HTTP_STATUS_FAILED_TO_ROUTE);
+                });
       }).bind(this));
 
       return cancellable;
     },
 
     sendHttpRequest: function(mmsConnection, method, url, istream, proxyFilter,
                               callback) {
       let releaseMmsConnectionAndCallback = function(httpStatus, data) {
@@ -812,16 +892,18 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
                                              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_FAILED_TO_ROUTE:
+          return _MMS_ERROR_FAILED_TO_ROUTE;
         case HTTP_STATUS_OK:
           return MMS.MMS_PDU_ERROR_OK;
         default:
           return defaultStatus;
       }
     }
   };