Bug 776294 - B2G 3G: Configure APN settings through SettingsService. r=philikon
authorJose Antonio Olivera Ortega <josea.olivera@gmail.com>
Wed, 01 Aug 2012 07:54:04 -0700
changeset 101105 4c7c41b33485b3f3e6312c1cc584771a0a7e59e2
parent 101104 d2ee4c12c0b34c9735ac3c8453bb155e907f4198
child 101106 3041cf7e42ed082c615580a95e70e4945bb6e7c3
push id23216
push userryanvm@gmail.com
push dateWed, 01 Aug 2012 21:51:28 +0000
treeherdermozilla-central@7fe96a690596 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilikon
bugs776294
milestone17.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 776294 - B2G 3G: Configure APN settings through SettingsService. r=philikon
b2g/chrome/content/settings.js
dom/system/gonk/RadioInterfaceLayer.js
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -60,24 +60,17 @@ SettingsListener.observe('audio.volume.m
 // =================== Languages ====================
 SettingsListener.observe('language.current', 'en-US', function(value) {
   Services.prefs.setCharPref('intl.accept_languages', value);
 });
 
 
 // =================== RIL ====================
 (function RILSettingsToPrefs() {
-  ['ril.data.enabled', 'ril.data.roaming.enabled'].forEach(function(key) {
-    SettingsListener.observe(key, false, function(value) {
-      Services.prefs.setBoolPref(key, value);
-    });
-  });
-
-  let strPrefs = ['ril.data.apn', 'ril.data.user', 'ril.data.passwd',
-                  'ril.data.mmsc', 'ril.data.mmsproxy'];
+  let strPrefs = ['ril.data.mmsc', 'ril.data.mmsproxy'];
   strPrefs.forEach(function(key) {
     SettingsListener.observe(key, "", function(value) {
       Services.prefs.setCharPref(key, value);
     });
   });
 
   ['ril.data.mmsport'].forEach(function(key) {
     SettingsListener.observe(key, null, function(value) {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -170,16 +170,28 @@ function RadioInterfaceLayer() {
                      signalStrength: null,
                      relSignalStrength: null},
   };
 
   // Read the 'ril.radio.disabled' setting in order to start with a known
   // value at boot time.
   gSettingsService.getLock().get("ril.radio.disabled", this);
 
+  // Read the APN data form the setting DB.
+  gSettingsService.getLock().get("ril.data.apn", this);
+  gSettingsService.getLock().get("ril.data.user", this);
+  gSettingsService.getLock().get("ril.data.passwd", this);
+  gSettingsService.getLock().get("ril.data.roaming_enabled", this);
+  gSettingsService.getLock().get("ril.data.enabled", this);
+  this._dataCallSettingsToRead = ["ril.data.enabled",
+                                  "ril.data.roaming_enabled",
+                                  "ril.data.apn",
+                                  "ril.data.user",
+                                  "ril.data.passwd"];
+
   for each (let msgname in RIL_IPC_MSG_NAMES) {
     ppmm.addMessageListener(msgname, this);
   }
   Services.obs.addObserver(this, "xpcom-shutdown", false);
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
 
   this._sentSmsEnvelopes = {};
 
@@ -327,17 +339,17 @@ RadioInterfaceLayer.prototype = {
         this.updateVoiceConnection(message);
         break;
       case "dataregistrationstatechange":
         this.updateDataConnection(message);
         break;
       case "datacallerror":
         // 3G Network revoked the data connection, possible unavailable APN
         debug("Received data registration error message. Failed APN " +
-              Services.prefs.getCharPref("ril.data.apn"));
+              this.dataCallSettings["apn"]);
         RILNetworkInterface.reset();
         break;
       case "signalstrengthchange":
         this.handleSignalStrengthChange(message);
         break;
       case "operatorchange":
         this.handleOperatorChange(message);
         break;
@@ -511,32 +523,16 @@ RadioInterfaceLayer.prototype = {
       if (!state.batch) {
         ppmm.sendAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
       }
       return true;
     }
     return false;
   },
 
-  _isDataEnabled: function _isDataEnabled() {
-    try {
-      return Services.prefs.getBoolPref("ril.data.enabled");
-    } catch(ex) {
-      return false;
-    }
-  },
-
-  _isDataRoamingEnabled: function _isDataRoamingEnabled() {
-    try {
-      return Services.prefs.getBoolPref("ril.data.roaming.enabled");
-    } catch(ex) {
-      return false;
-    }
-  },
-
   updateDataConnection: function updateDataConnection(state) {
     let data = this.rilContext.data;
     if (!state || state.regState == RIL.NETWORK_CREG_STATE_UNKNOWN) {
       data.connected = false;
       data.emergencyCallsOnly = false;
       data.roaming = false;
       data.network = null;
       data.type = null;
@@ -545,31 +541,30 @@ RadioInterfaceLayer.prototype = {
       ppmm.sendAsyncMessage("RIL:DataInfoChanged", data);
       return false;
     }
     data.roaming =
       (state.regState == RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING);
     data.type = RIL.GECKO_RADIO_TECH[state.radioTech] || null;
     ppmm.sendAsyncMessage("RIL:DataInfoChanged", data);
 
-    if (!this._isDataEnabled()) {
+    if (!this.dataCallSettings["enabled"]) {
       return false;
     }
 
     let isRegistered =
       state.regState == RIL.NETWORK_CREG_STATE_REGISTERED_HOME ||
-        (this._isDataRoamingEnabled() &&
+        (this.dataCallSettings["roaming_enabled"] &&
          state.regState == RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING);
     let haveDataConnection =
       state.radioTech != RIL.NETWORK_CREG_TECH_UNKNOWN;
 
     if (isRegistered && haveDataConnection) {
       debug("Radio is ready for data connection.");
-      // RILNetworkInterface will ignore this if it's already connected.
-      RILNetworkInterface.connect();
+      this.updateRILNetworkInterface();
     }
     return false;
   },
 
   handleSignalStrengthChange: function handleSignalStrengthChange(message) {
     // TODO CDMA, EVDO, LTE, etc. (see bug 726098)
     this.rilContext.voice.signalStrength = message.gsmDBM;
     this.rilContext.voice.relSignalStrength = message.gsmRelative;
@@ -633,16 +628,49 @@ RadioInterfaceLayer.prototype = {
       this.setRadioEnabled(true);
     }
     if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_READY &&
         !this._radioEnabled) {
       this.setRadioEnabled(false);
     }
   },
 
+  updateRILNetworkInterface: function updateRILNetworkInterface() {
+    if (this._dataCallSettingsToRead.length) {
+      debug("We haven't read completely the APN data from the " +
+            "settings DB yet. Wait for that.");
+      return;
+    } 
+
+    // This check avoids data call connection if the radio is not ready 
+    // yet after toggling off airplane mode. 
+    if (this.rilContext.radioState != RIL.GECKO_RADIOSTATE_READY) {
+      return; 
+    }
+
+    // We only watch at "ril.data.enabled" flag changes for connecting or
+    // disconnecting the data call. If the value of "ril.data.enabled" is
+    // 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) {
+      debug("Data call settings: disconnect data call.");
+      RILNetworkInterface.disconnect();
+    }
+    if (this.dataCallSettings["enabled"] && !RILNetworkInterface.connected) {
+      debug("Data call settings connect data call.");
+      RILNetworkInterface.connect(this.dataCallSettings);
+    }
+  },
+
   /**
    * Track the active call and update the audio system as its state changes.
    */
   _activeCall: null,
   updateCallAudioState: function updateCallAudioState() {
     if (!this._activeCall) {
       // Disable audio.
       gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
@@ -919,43 +947,16 @@ RadioInterfaceLayer.prototype = {
   /**
    * Handle data call list.
    */
   handleDataCallList: function handleDataCallList(message) {
     this._deliverDataCallCallback("receiveDataCallList",
                                   [message.datacalls, message.datacalls.length]);
   },
 
-  /**
-   * Handle setting changes.
-   */
-  handleMozSettingsChanged: function handleMozSettingsChanged(setting) {
-    switch (setting.key) {
-      case "ril.radio.disabled":
-        this._radioEnabled = !setting.value;
-        this._ensureRadioState();
-        break;
-      case "ril.data.enabled":
-        // We only watch at "ril.data.enabled" flag changes for connecting or
-        // disconnecting the data call. If the value of "ril.data.enabled" is
-        // 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 (!setting.value && RILNetworkInterface.connected) {
-          debug("Data call settings: disconnect data call.");
-          RILNetworkInterface.disconnect();
-        }
-        if (setting.value && !RILNetworkInterface.connected) {
-          debug("Data call settings connect data call.");
-          RILNetworkInterface.connect();
-        }
-        break;
-    }
-  },
-
   handleICCGetCardLock: function handleICCGetCardLock(message) {
     ppmm.sendAsyncMessage("RIL:GetCardLock:Return:OK", message);
   },
 
   handleICCSetCardLock: function handleICCSetCardLock(message) {
     ppmm.sendAsyncMessage("RIL:SetCardLock:Return:OK", message);
   },
 
@@ -983,17 +984,17 @@ RadioInterfaceLayer.prototype = {
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     switch (topic) {
       case kMozSettingsChangedObserverTopic:
         let setting = JSON.parse(data);
-        this.handleMozSettingsChanged(setting);
+        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();
         ppmm = null;
         Services.obs.removeObserver(this, "xpcom-shutdown");
@@ -1003,29 +1004,57 @@ RadioInterfaceLayer.prototype = {
   },
 
   // nsISettingsServiceCallback
 
   // Flag to determine the radio state to start with when we boot up. It
   // corresponds to the 'ril.radio.disabled' setting from the UI.
   _radioEnabled: null,
 
+  // APN data for making data calls.
+  dataCallSettings: {},
+  _dataCallSettingsToRead: [],
+  _oldRilDataEnabledState: null,
+  
   handle: function handle(aName, aResult) {
-    if (aName == "ril.radio.disabled") {
-      debug("'ril.radio.disabled' is " + aResult);
-      this._radioEnabled = !aResult;
-      this._ensureRadioState();
-    }
+    switch(aName) {
+      case "ril.radio.disabled":
+        debug("'ril.radio.disabled' is now " + aResult);
+        this._radioEnabled = !aResult;
+        this._ensureRadioState();
+        break;
+      case "ril.data.enabled":
+        this._oldRilDataEnabledState = this.dataCallSettings["enabled"];
+        // Fall through!
+      case "ril.data.roaming_enabled":
+      case "ril.data.apn":
+      case "ril.data.user":
+      case "ril.data.passwd":
+        let key = aName.slice(9);
+        this.dataCallSettings[key] = aResult;
+        debug("'" + aName + "'" + " is now " + this.dataCallSettings[key]);
+        let index = this._dataCallSettingsToRead.indexOf(aName);
+        if (index != -1) {
+          this._dataCallSettingsToRead.splice(index, 1);
+        }
+        this.updateRILNetworkInterface();
+        break;
+    };
   },
-
+    
   handleError: function handleError(aErrorMessage) {
-    debug("There was an error reading the 'ril.radio.disabled' setting., " +
-          "default to radio on.");
+    debug("There was an error while reading RIL settings.");
+
+    // Default radio to on.
     this._radioEnabled = true;
     this._ensureRadioState();
+
+    // Clean data call setting. 
+    this.dataCallSettings = {};
+    this.dataCallSettings["enabled"] = false; 
   },
 
   // nsIRadioWorker
 
   worker: null,
 
   // nsIRadioInterfaceLayer
 
@@ -1762,56 +1791,54 @@ let RILNetworkInterface = {
   },
 
   // Helpers
 
   cid: null,
   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() {
+  connect: function connect(options) {
     if (this.connecting ||
         this.state == RIL.GECKO_NETWORK_STATE_CONNECTED ||
         this.state == RIL.GECKO_NETWORK_STATE_SUSPENDED) {
       return;
     }
+
     if (!this.registeredAsDataCallCallback) {
       this.mRIL.registerDataCallCallback(this);
       this.registeredAsDataCallCallback = true;
     }
 
-    let apn, user, passwd;
-    // Eventually these values would be retrieved from the user's preferences
-    // via the settings API. For now we just use Gecko's preferences.
-    try {
-      apn = Services.prefs.getCharPref("ril.data.apn");
-      user = Services.prefs.getCharPref("ril.data.user");
-      passwd = Services.prefs.getCharPref("ril.data.passwd");
-    } catch (ex) {
-      debug("No APN settings found, not going to set up data connection.");
-      return;
+    if (options) {
+      // Save the APN data locally for using them in connection retries. 
+      this.dataCallSettings = options;
     }
-    debug("Going to set up data connection with APN " + apn);
+
+    debug("Going to set up data connection with APN " + this.dataCallSettings["apn"]);
     this.mRIL.setupDataCall(RIL.DATACALL_RADIOTECHNOLOGY_GSM,
-                            apn, user, passwd,
+                            this.dataCallSettings["apn"], 
+                            this.dataCallSettings["user"], 
+                            this.dataCallSettings["passwd"],
                             RIL.DATACALL_AUTH_PAP_OR_CHAP, "IP");
     this.connecting = true;
   },
 
   reset: function reset() {
     let apnRetryTimer;
     this.connecting = false;
     // We will retry the connection in increasing times