Bug 1094572 - B2G RIL: handle data call fail cause accordingly. r=echen
authorJessica Jong <jjong@mozilla.com>
Mon, 24 Nov 2014 10:15:25 +0800
changeset 241437 8af17af9fc2e838662bed22ff3cf0b269c6e50f4
parent 241436 3deea6e95e6a74b03d8dbc37cf9f1bac3a6723f4
child 241438 baaa1bc7c2fd9532e1cc9f6bdb58f61f5c8047fd
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechen
bugs1094572
milestone36.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 1094572 - B2G RIL: handle data call fail cause accordingly. r=echen
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -82,16 +82,18 @@ const DOM_MOBILE_MESSAGE_DELIVERY_RECEIV
 const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
 const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
 const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
 
 const RADIO_POWER_OFF_TIMEOUT = 30000;
 const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
 const HW_DEFAULT_CLIENT_ID = 0;
 
+const INT32_MAX = 2147483647;
+
 const RIL_IPC_ICCMANAGER_MSG_NAMES = [
   "RIL:GetRilContext",
   "RIL:SendStkResponse",
   "RIL:SendStkMenuSelection",
   "RIL:SendStkTimerExpiration",
   "RIL:SendStkEventDownload",
   "RIL:GetCardLockState",
   "RIL:UnlockCardLock",
@@ -771,23 +773,22 @@ XPCOMUtils.defineLazyGetter(this, "gData
         newConnHandler.updateRILNetworkInterface();
       }).bind(this);
 
       if (this._dataEnabled) {
         oldSettings.oldEnabled = oldSettings.enabled;
         oldSettings.enabled = false;
       }
 
-      if (oldConnHandler.anyDataConnected()) {
+      if (oldConnHandler.deactivateDataCalls()) {
         this._pendingDataCallRequest = applyPendingDataSettings;
         if (DEBUG) {
           this.debug("_handleDataClientIdChange: existing data call(s) active" +
                      ", wait for them to get disconnected.");
         }
-        oldConnHandler.deactivateDataCalls();
         return;
       }
 
       applyPendingDataSettings();
     },
 
     _shutdown: function() {
       for (let handler of this._connectionHandlers) {
@@ -1140,28 +1141,16 @@ DataConnectionHandler.prototype = {
       if (dataCall.state != RIL.GECKO_NETWORK_STATE_UNKNOWN &&
           dataCall.state != RIL.GECKO_NETWORK_STATE_DISCONNECTED) {
         return false;
       }
     }
     return true;
   },
 
-  /**
-   * Check if there is any activated data connection.
-   */
-  anyDataConnected: function() {
-    for (let i = 0; i < this._dataCalls.length; i++) {
-      if (this._dataCalls[i].state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
-        return true;
-      }
-    }
-    return false;
-  },
-
   updateApnSettings: function(newApnSettings) {
     if (!newApnSettings) {
       return;
     }
     if (this._pendingApnSettings) {
       // Change of apn settings in process, just update to the newest.
       this._pengingApnSettings = newApnSettings;
       return;
@@ -1330,27 +1319,31 @@ DataConnectionHandler.prototype = {
     }
 
     networkInterface.disconnect();
   },
 
   deactivateDataCalls: function() {
     let dataDisconnecting = false;
     this.dataNetworkInterfaces.forEach(function(networkInterface) {
-      if (networkInterface.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
+      if (networkInterface.enabled) {
+        if (networkInterface.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
+          dataDisconnecting = true;
+        }
         networkInterface.disconnect();
-        dataDisconnecting = true;
       }
     });
 
     // No data calls exist. It's safe to proceed the pending radio power off
     // request.
     if (gRadioEnabledController.isDeactivatingDataCalls() && !dataDisconnecting) {
       gRadioEnabledController.finishDeactivatingDataCalls(this.clientId);
     }
+
+    return dataDisconnecting;
   },
 
   /**
    * Handle data errors.
    */
   handleDataCallError: function(message) {
     // Notify data call error only for data APN
     let networkInterface = this.dataNetworkInterfaces.get("default");
@@ -3807,19 +3800,36 @@ DataCall.prototype = {
 
   // Holds the pdp type sent to ril worker.
   pdptype: null,
 
   // Holds the authentication type sent to ril worker.
   chappap: null,
 
   dataCallError: function(message) {
-    if (DEBUG) this.debug("Data call error on APN: " + message.apn);
+    if (DEBUG) {
+      this.debug("Data call error on APN " + message.apn + ": " +
+                 message.errorMsg + " (" + message.status + "), retry time: " +
+                 message.suggestedRetryTime);
+    }
     this.state = RIL.GECKO_NETWORK_STATE_DISCONNECTED;
-    this.retry();
+
+    if (this.requestedNetworkIfaces.length === 0) {
+      if (DEBUG) this.debug("This DataCall is not requested anymore.");
+      return;
+    }
+
+    // For suggestedRetryTime, the value of INT32_MAX(0x7fffffff) means no retry.
+    if (message.suggestedRetryTime === INT32_MAX ||
+        this.isPermanentFail(message.status, message.errorMsg)) {
+      if (DEBUG) this.debug("Data call error: no retry needed.");
+      return;
+    }
+
+    this.retry(message.suggestedRetryTime);
   },
 
   dataCallStateChanged: function(datacall) {
     if (DEBUG) {
       this.debug("Data call ID: " + datacall.cid + ", interface name: " +
                  datacall.ifname + ", APN name: " + datacall.apn + ", state: " +
                  datacall.state);
     }
@@ -3827,16 +3837,17 @@ DataCall.prototype = {
     if (this.state == datacall.state &&
         datacall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) {
       return;
     }
 
     switch (datacall.state) {
       case RIL.GECKO_NETWORK_STATE_CONNECTED:
         if (this.state == RIL.GECKO_NETWORK_STATE_CONNECTING) {
+          this.apnRetryCounter = 0;
           this.linkInfo.cid = datacall.cid;
 
           if (this.requestedNetworkIfaces.length === 0) {
             if (DEBUG) {
               this.debug("State is connected, but no network interface requested" +
                          " this DataCall");
             }
             this.deactivate();
@@ -3914,16 +3925,39 @@ DataCall.prototype = {
     dump("-*- DataCall[" + this.clientId + ":" + this.apnProfile.apn + "]: " +
       s + "\n");
   },
 
   get connected() {
     return this.state == RIL.GECKO_NETWORK_STATE_CONNECTED;
   },
 
+  isPermanentFail: function(dataFailCause, errorMsg) {
+    // Check ril.h for 'no retry' data call fail causes.
+    if (errorMsg === RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE ||
+        errorMsg === RIL.GECKO_ERROR_INVALID_PARAMETER ||
+        dataFailCause === RIL.DATACALL_FAIL_OPERATOR_BARRED ||
+        dataFailCause === RIL.DATACALL_FAIL_MISSING_UKNOWN_APN ||
+        dataFailCause === RIL.DATACALL_FAIL_UNKNOWN_PDP_ADDRESS_TYPE ||
+        dataFailCause === RIL.DATACALL_FAIL_USER_AUTHENTICATION ||
+        dataFailCause === RIL.DATACALL_FAIL_ACTIVATION_REJECT_GGSN ||
+        dataFailCause === RIL.DATACALL_FAIL_SERVICE_OPTION_NOT_SUPPORTED ||
+        dataFailCause === RIL.DATACALL_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED ||
+        dataFailCause === RIL.DATACALL_FAIL_NSAPI_IN_USE ||
+        dataFailCause === RIL.DATACALL_FAIL_ONLY_IPV4_ALLOWED ||
+        dataFailCause === RIL.DATACALL_FAIL_ONLY_IPV6_ALLOWED ||
+        dataFailCause === RIL.DATACALL_FAIL_PROTOCOL_ERRORS ||
+        dataFailCause === RIL.DATACALL_FAIL_RADIO_POWER_OFF ||
+        dataFailCause === RIL.DATACALL_FAIL_TETHERED_CALL_ACTIVE) {
+      return true;
+    }
+
+    return false;
+  },
+
   inRequestedTypes: function(type) {
     for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
       if (this.requestedNetworkIfaces[i].type == type) {
         return true;
       }
     }
     return false;
   },
@@ -4040,31 +4074,36 @@ DataCall.prototype = {
       user: this.apnProfile.user,
       passwd: this.apnProfile.password,
       chappap: authType,
       pdptype: pdpType
     });
     this.state = RIL.GECKO_NETWORK_STATE_CONNECTING;
   },
 
-  retry: function() {
+  retry: function(suggestedRetryTime) {
     let apnRetryTimer;
 
     // We will retry the connection in increasing times
     // based on the function: time = A * numer_of_retries^2 + B
     if (this.apnRetryCounter >= this.NETWORK_APNRETRY_MAXRETRIES) {
       this.apnRetryCounter = 0;
       this.timer = null;
       if (DEBUG) this.debug("Too many APN Connection retries - STOP retrying");
       return;
     }
 
-    apnRetryTimer = this.NETWORK_APNRETRY_FACTOR *
-                    (this.apnRetryCounter * this.apnRetryCounter) +
-                    this.NETWORK_APNRETRY_ORIGIN;
+    // If there is a valid suggestedRetryTime, override the retry timer.
+    if (suggestedRetryTime !== undefined && suggestedRetryTime >= 0) {
+      apnRetryTimer = suggestedRetryTime / 1000;
+    } else {
+      apnRetryTimer = this.NETWORK_APNRETRY_FACTOR *
+                      (this.apnRetryCounter * this.apnRetryCounter) +
+                      this.NETWORK_APNRETRY_ORIGIN;
+    }
     this.apnRetryCounter++;
     if (DEBUG) {
       this.debug("Data call - APN Connection Retry Timer (secs-counter): " +
                  apnRetryTimer + "-" + this.apnRetryCounter);
     }
 
     if (this.timer == null) {
       // Event timer for connection retries
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -4136,16 +4136,20 @@ RilObject.prototype = {
     return "identical";
   },
 
   _processDataCallList: function(datacalls, newDataCallOptions) {
     // Check for possible PDP errors: We check earlier because the datacall
     // can be removed if is the same as the current one.
     for each (let newDataCall in datacalls) {
       if (newDataCall.status != DATACALL_FAIL_NONE) {
+        if (newDataCallOptions) {
+          newDataCallOptions.status = newDataCall.status;
+          newDataCallOptions.suggestedRetryTime = newDataCall.suggestedRetryTime;
+        }
         this._sendDataCallError(newDataCallOptions || newDataCall,
                                 newDataCall.status);
       }
     }
 
     for each (let currentDataCall in this.currentDataCalls) {
       let updatedDataCall;
       if (datacalls) {