Bug 782513 - B2G RIL: Support multiple instances of RILNetworkInterface. r=philikon
authorShian-Yow Wu <swu@mozilla.com>
Wed, 26 Sep 2012 20:52:21 +0800
changeset 108246 a58c7a71705735b967a6a8a2600e69067311b547
parent 108245 e305a1b3a777c2e2f52390cdff618877a1defc27
child 108247 b8fa9968ad3d901cd15a31aa0e405f478da612d6
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersphilikon
bugs782513
milestone18.0a1
Bug 782513 - B2G RIL: Support multiple instances of RILNetworkInterface. r=philikon
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -23,16 +23,18 @@ Cu.import("resource://gre/modules/Servic
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
 // set to true in ril_consts.js to see debug messages
 const DEBUG = RIL.DEBUG_RIL;
 
 const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
+const RILNETWORKINTERFACE_CID =
+  Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer;
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsDeliveredObserverTopic         = "sms-delivered";
@@ -88,16 +90,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
+                                   "@mozilla.org/network/manager;1",
+                                   "nsINetworkManager");
+
 XPCOMUtils.defineLazyGetter(this, "WAP", function () {
   let WAP = {};
   Cu.import("resource://gre/modules/WapPushManager.js", WAP);
   return WAP;
 });
 
 function convertRILCallState(state) {
   switch (state) {
@@ -145,16 +151,20 @@ XPCOMUtils.defineLazyGetter(this, "gAudi
     //TODO on the phone this should not fall back as silently.
     debug("Using fake audio manager.");
     return FakeAudioManager;
   }
 });
 
 
 function RadioInterfaceLayer() {
+  this.dataNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
+  this.mmsNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS);
+  this.suplNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL);
+
   debug("Starting RIL Worker");
   this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
   this.worker.onerror = this.onerror.bind(this);
   this.worker.onmessage = this.onmessage.bind(this);
 
   this.rilContext = {
     radioState:     RIL.GECKO_RADIOSTATE_UNAVAILABLE,
     cardState:      RIL.GECKO_CARDSTATE_UNAVAILABLE,
@@ -614,17 +624,17 @@ RadioInterfaceLayer.prototype = {
   updateDataConnection: function updateDataConnection(newInfo) {
     let dataInfo = this.rilContext.data;
     dataInfo.state = newInfo.state;
     dataInfo.roaming = newInfo.roaming;
     dataInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
     dataInfo.type = newInfo.type;
     // For the data connection, the `connected` flag indicates whether
     // there's an active data call.
-    dataInfo.connected = RILNetworkInterface.connected;
+    dataInfo.connected = this.dataNetworkInterface.connected;
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
     if (newInfo.regState == RIL.NETWORK_CREG_STATE_UNKNOWN) {
       dataInfo.network = null;
       dataInfo.signalStrength = null;
       dataInfo.relSignalStrength = null;
     }
@@ -641,24 +651,22 @@ RadioInterfaceLayer.prototype = {
     }
     this.updateRILNetworkInterface();
   },
 
   /**
    * Handle data errors
    */
   handleDataCallError: function handleDataCallError(message) {
-    if (message.apn != this.dataCallSettings["apn"]) {
-      return;
+    // Notify data call error only for data APN
+    if (message.apn == this.dataCallSettings["apn"]) {
+      ppmm.broadcastAsyncMessage("RIL:DataError", message);
     }
 
-    // 3G Network revoked the data connection, possible unavailable APN
-    RILNetworkInterface.reset();
-    // Notify datacall error
-    ppmm.broadcastAsyncMessage("RIL:DataError", message);
+    this._deliverDataCallCallback("dataCallError", [message]);
   },
 
   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;
@@ -792,22 +800,22 @@ RadioInterfaceLayer.prototype = {
     // true and any of the remaining flags change the setting application
     // should turn this flag to false and then to true in order to reload
     // the new values and reconnect the data call.
     if (this._oldRilDataEnabledState == this.dataCallSettings["enabled"]) {
       debug("No changes for ril.data.enabled flag. Nothing to do.");
       return;
     }
 
-    if (!this.dataCallSettings["enabled"] && RILNetworkInterface.connected) {
+    if (!this.dataCallSettings["enabled"] && this.dataNetworkInterface.connected) {
       debug("Data call settings: disconnect data call.");
-      RILNetworkInterface.disconnect();
+      this.dataNetworkInterface.disconnect();
       return;
     }
-    if (!this.dataCallSettings["enabled"] || RILNetworkInterface.connected) {
+    if (!this.dataCallSettings["enabled"] || this.dataNetworkInterface.connected) {
       debug("Data call settings: nothing to do.");
       return;
     }
     let dataInfo = this.rilContext.data;
     let isRegistered =
       dataInfo.state == RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED;
     let haveDataConnection =
       dataInfo.type != RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN;
@@ -817,17 +825,17 @@ RadioInterfaceLayer.prototype = {
       return;
     }
     if (dataInfo.roaming && !this.dataCallSettings["roaming_enabled"]) {
       debug("We're roaming, but data roaming is disabled.");
       return;
     }
 
     debug("Data call settings: connect data call.");
-    RILNetworkInterface.connect(this.dataCallSettings);
+    this.dataNetworkInterface.connect(this.dataCallSettings);
   },
 
   /**
    * Track the active call and update the audio system as its state changes.
    */
   _activeCall: null,
   updateCallAudioState: function updateCallAudioState() {
     if (!this._activeCall) {
@@ -1108,19 +1116,20 @@ RadioInterfaceLayer.prototype = {
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     let data = this.rilContext.data;
 
-    if (datacall.ifname) {
+    if (datacall.ifname &&
+        datacall.apn == this.dataCallSettings["apn"]) {
       data.connected = (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED);
-      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", data);    
+      ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", data);
     }
 
     this._deliverDataCallCallback("dataCallStateChanged",
                                   [datacall]);
   },
 
   /**
    * Handle data call list.
@@ -1187,17 +1196,20 @@ RadioInterfaceLayer.prototype = {
       case kMozSettingsChangedObserverTopic:
         let setting = JSON.parse(data);
         this.handle(setting.key, setting.value);
         break;
       case "xpcom-shutdown":
         for each (let msgname in RIL_IPC_MSG_NAMES) {
           ppmm.removeMessageListener(msgname, this);
         }
-        RILNetworkInterface.shutdown();
+        // 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;
     }
   },
 
   // Flag to determine whether the UI's system app is ready to receive
@@ -1884,18 +1896,28 @@ RadioInterfaceLayer.prototype = {
     let requestId = Math.floor(Math.random() * 1000);
     this._contactsCallbacks[requestId] = callback;
     this.worker.postMessage({rilMessageType: "getICCContacts",
                              contactType: contactType,
                              requestId: requestId});
   }
 };
 
-let RILNetworkInterface = {
+function RILNetworkInterface(ril, type)
+{
+  this.mRIL = ril;
+  this.type = type;
+}
 
+RILNetworkInterface.prototype = {
+  classID:   RILNETWORKINTERFACE_CID,
+  classInfo: XPCOMUtils.generateCI({classID: RILNETWORKINTERFACE_CID,
+                                    classDescription: "RILNetworkInterface",
+                                    interfaces: [Ci.nsINetworkInterface,
+                                                 Ci.nsIRIODataCallback]}),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface,
                                          Ci.nsIRILDataCallback]),
 
   // nsINetworkInterface
 
   NETWORK_STATE_UNKNOWN:       Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
   NETWORK_STATE_CONNECTING:    Ci.nsINetworkInterface.CONNECTING,
   NETWORK_STATE_CONNECTED:     Ci.nsINetworkInterface.CONNECTED,
@@ -1937,47 +1959,65 @@ let RILNetworkInterface = {
   dns2: null,
 
   httpProxyHost: null,
 
   httpProxyPort: null,
 
   // nsIRILDataCallback
 
+  dataCallError: function dataCallError(message) {
+    if (message.apn != this.dataCallSettings["apn"]) {
+      return;
+    }
+    debug("Data call error on APN: " + message.apn);
+    this.reset();
+  },
+
   dataCallStateChanged: function dataCallStateChanged(datacall) {
-    debug("Data call ID: " + datacall.cid + ", interface name: " + datacall.ifname);
+    if (datacall.apn != this.dataCallSettings["apn"]) {
+      return;
+    }
+    debug("Data call ID: " + datacall.cid + ", interface name: " +
+          datacall.ifname + ", APN name: " + datacall.apn);
     if (this.connecting &&
         (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTING ||
          datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED)) {
       this.connecting = false;
       this.cid = datacall.cid;
       this.name = datacall.ifname;
       this.ip = datacall.ip;
       this.netmask = datacall.netmask;
       this.broadcast = datacall.broadcast;
       this.gateway = datacall.gw;
       if (datacall.dns) {
         this.dns1 = datacall.dns[0];
         this.dns2 = datacall.dns[1];
       }
       if (!this.registeredAsNetworkInterface) {
-        let networkManager = Cc["@mozilla.org/network/manager;1"]
-                               .getService(Ci.nsINetworkManager);
-        networkManager.registerNetworkInterface(this);
+        gNetworkManager.registerNetworkInterface(this);
         this.registeredAsNetworkInterface = true;
       }
     }
     if (this.cid != datacall.cid) {
       return;
     }
     if (this.state == datacall.state) {
       return;
     }
 
     this.state = datacall.state;
+
+    if (this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN &&
+       this.registeredAsNetworkInterface) {
+      gNetworkManager.unregisterNetworkInterface(this);
+      this.registeredAsNetworkInterface = false;
+      return;
+    }
+
     Services.obs.notifyObservers(this,
                                  kNetworkInterfaceStateChangedTopic,
                                  null);
   },
 
   receiveDataCallList: function receiveDataCallList(dataCalls, length) {
   },
 
@@ -1987,39 +2027,32 @@ let RILNetworkInterface = {
   registeredAsDataCallCallback: false,
   registeredAsNetworkInterface: false,
   connecting: false,
   dataCallSettings: {},
 
   // APN failed connections. Retry counter
   apnRetryCounter: 0,
 
-  get mRIL() {
-    delete this.mRIL;
-    return this.mRIL = Cc["@mozilla.org/telephony/system-worker-manager;1"]
-                         .getService(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIRadioInterfaceLayer);
-  },
-
   get connected() {
     return this.state == RIL.GECKO_NETWORK_STATE_CONNECTED;
   },
 
   connect: function connect(options) {
     if (this.connecting || this.connected) {
       return;
     }
 
     if (!this.registeredAsDataCallCallback) {
       this.mRIL.registerDataCallCallback(this);
       this.registeredAsDataCallCallback = true;
     }
 
     if (options) {
-      // Save the APN data locally for using them in connection retries. 
+      // Save the APN data locally for using them in connection retries.
       this.dataCallSettings = options;
     }
 
     this.httpProxyHost = this.dataCallSettings["httpProxyHost"];
     this.httpProxyPort = this.dataCallSettings["httpProxyPort"];
 
     debug("Going to set up data connection with APN " + this.dataCallSettings["apn"]);
     this.mRIL.setupDataCall(RIL.DATACALL_RADIOTECHNOLOGY_GSM,
@@ -2064,17 +2097,17 @@ let RILNetworkInterface = {
     }
     let reason = RIL.DATACALL_DEACTIVATE_NO_REASON;
     debug("Going to disconnet data connection " + this.cid);
     this.mRIL.deactivateDataCall(this.cid, reason);
   },
 
   // Entry method for timer events. Used to reconnect to a failed APN
   notify: function(timer) {
-    RILNetworkInterface.connect();
+    this.connect();
   },
 
   shutdown: function() {
     this.timer = null;
   }
 
 };
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2967,34 +2967,44 @@ let RIL = {
     for each (let currentDataCall in this.currentDataCalls) {
       let updatedDataCall;
       if (datacalls) {
         updatedDataCall = datacalls[currentDataCall.cid];
         delete datacalls[currentDataCall.cid];
       }
 
       if (!updatedDataCall) {
-        delete this.currentDataCalls[currentDataCall.callIndex];
         currentDataCall.state = GECKO_NETWORK_STATE_DISCONNECTED;
         currentDataCall.rilMessageType = "datacallstatechange";
         this.sendDOMMessage(currentDataCall);
         continue;
       }
 
+      if (updatedDataCall && !updatedDataCall.ifname) {
+        delete this.currentDataCalls[currentDataCall.cid];
+        currentDataCall.state = GECKO_NETWORK_STATE_UNKNOWN;
+        currentDataCall.rilMessageType = "datacallstatechange";
+        this.sendDOMMessage(currentDataCall);
+        continue;
+      }
+
       this._setDataCallGeckoState(updatedDataCall);
       if (updatedDataCall.state != currentDataCall.state) {
         currentDataCall.status = updatedDataCall.status;
         currentDataCall.active = updatedDataCall.active;
         currentDataCall.state = updatedDataCall.state;
         currentDataCall.rilMessageType = "datacallstatechange";
         this.sendDOMMessage(currentDataCall);
       }
     }
 
     for each (let newDataCall in datacalls) {
+      if (!newDataCall.ifname) {
+        continue;
+      }
       this.currentDataCalls[newDataCall.cid] = newDataCall;
       this._setDataCallGeckoState(newDataCall);
       if (newDataCallOptions) {
         newDataCall.radioTech = newDataCallOptions.radioTech;
         newDataCall.apn = newDataCallOptions.apn;
         newDataCall.user = newDataCallOptions.user;
         newDataCall.passwd = newDataCallOptions.passwd;
         newDataCall.chappap = newDataCallOptions.chappap;
@@ -4066,17 +4076,17 @@ RIL[REQUEST_GET_IMEISV] = function REQUE
 RIL[REQUEST_ANSWER] = null;
 RIL[REQUEST_DEACTIVATE_DATA_CALL] = function REQUEST_DEACTIVATE_DATA_CALL(length, options) {
   if (options.rilRequestError) {
     return;
   }
 
   let datacall = this.currentDataCalls[options.cid];
   delete this.currentDataCalls[options.cid];
-  datacall.state = GECKO_NETWORK_STATE_DISCONNECTED;
+  datacall.state = GECKO_NETWORK_STATE_UNKNOWN;
   datacall.rilMessageType = "datacallstatechange";
   this.sendDOMMessage(datacall);
 };
 RIL[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILITY_LOCK(length, options) {
   options.success = options.rilRequestError == 0;
   if (!options.success) {
     options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
   }