Bug 963054 - Wait card state initialization. r=hsinyi
authorSzu-Yu Chen [:aknow] <szchen@mozilla.com>
Fri, 14 Feb 2014 17:42:32 +0800
changeset 169315 85eba307aa43428c13378e2abb7d9d48add5263d
parent 169314 429c432f0fbf89ce9520c2e1fbd2cc71d6d5ae81
child 169316 c05ef4387fbd0f214a384a4edeafdf4d97e5782f
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewershsinyi
bugs963054
milestone30.0a1
Bug 963054 - Wait card state initialization. r=hsinyi
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/ril_consts.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -503,68 +503,92 @@ XPCOMUtils.defineLazyGetter(this, "gMess
         clientId: clientId,
         data: data
       });
     }
   };
 });
 
 XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
+  let _ril = null;
+  let _pendingMessages = [];  // For queueing "RIL =SetRadioEnabled" messages.
+  let _isProcessingPending = false;
+  let _timer = null;
+  let _request = null;
+  let _deactivatingDeferred = {};
+  let _initializedCardState = {};
+  let _allCardStateInitialized = !RILQUIRKS_RADIO_OFF_WO_CARD;
+
   return {
-    ril: null,
-    pendingMessages: [],  // For queueing "RIL:SetRadioEnabled" messages.
-    timer: null,
-    request: null,
-    deactivatingDeferred: {},
-
     init: function(ril) {
-      this.ril = ril;
+      _ril = ril;
+    },
+
+    receiveCardState: function(clientId) {
+      if (_allCardStateInitialized) {
+        return;
+      }
+
+      if (DEBUG) debug("RadioControl: receive cardState from " + clientId);
+      _initializedCardState[clientId] = true;
+      if (Object.keys(_initializedCardState).length == _ril.numRadioInterfaces) {
+        _allCardStateInitialized = true;
+        this._startProcessingPending();
+      }
     },
 
     receiveMessage: function(msg) {
-      if (DEBUG) debug("setRadioEnabled: receiveMessage: " + JSON.stringify(msg));
-      this.pendingMessages.push(msg);
-      if (this.pendingMessages.length === 1 && !this.isDeactivatingDataCalls()) {
-        this._processNextMessage();
-      }
+      if (DEBUG) debug("RadioControl: receiveMessage: " + JSON.stringify(msg));
+      _pendingMessages.push(msg);
+      this._startProcessingPending();
     },
 
     isDeactivatingDataCalls: function() {
-      return this.request !== null;
+      return _request !== null;
     },
 
     finishDeactivatingDataCalls: function(clientId) {
-      if (DEBUG) debug("setRadioEnabled: finishDeactivatingDataCalls: " + clientId);
-      let deferred = this.deactivatingDeferred[clientId];
+      if (DEBUG) debug("RadioControl: finishDeactivatingDataCalls: " + clientId);
+      let deferred = _deactivatingDeferred[clientId];
       if (deferred) {
         deferred.resolve();
       }
     },
 
+    _startProcessingPending: function() {
+      if (!_isProcessingPending) {
+        if (DEBUG) debug("RadioControl: start dequeue");
+        _isProcessingPending = true;
+        this._processNextMessage();
+      }
+    },
+
     _processNextMessage: function() {
-      if (this.pendingMessages.length === 0) {
+      if (_pendingMessages.length === 0 || !_allCardStateInitialized) {
+        if (DEBUG) debug("RadioControl: stop dequeue");
+        _isProcessingPending = false;
         return;
       }
 
-      let msg = this.pendingMessages.shift();
+      let msg = _pendingMessages.shift();
       this._handleMessage(msg);
     },
 
     _getNumCards: function() {
       let numCards = 0;
-      for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) {
+      for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
         if (this._isCardPresentAtClient(i)) {
           numCards++;
         }
       }
       return numCards;
     },
 
     _isCardPresentAtClient: function(clientId) {
-      let cardState = this.ril.getRadioInterface(clientId).rilContext.cardState;
+      let cardState = _ril.getRadioInterface(clientId).rilContext.cardState;
       return cardState !== RIL.GECKO_CARDSTATE_UNDETECTED &&
         cardState !== RIL.GECKO_CARDSTATE_UNKNOWN;
     },
 
     _isRadioAbleToEnableAtClient: function(clientId, numCards) {
       if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
         return true;
       }
@@ -581,19 +605,19 @@ XPCOMUtils.defineLazyGetter(this, "gRadi
       if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
         return true;
       }
 
       return false;
     },
 
     _handleMessage: function(msg) {
-      if (DEBUG) debug("setRadioEnabled: handleMessage: " + JSON.stringify(msg));
+      if (DEBUG) debug("RadioControl: handleMessage: " + JSON.stringify(msg));
       let clientId = msg.json.clientId || 0;
-      let radioInterface = this.ril.getRadioInterface(clientId);
+      let radioInterface = _ril.getRadioInterface(clientId);
 
       if (!radioInterface.isValidStateForSetRadioEnabled()) {
         radioInterface.setRadioEnabledResponse(msg.target, msg.json.data,
                                                "InvalidStateError");
         this._processNextMessage();
         return;
       }
 
@@ -608,84 +632,84 @@ XPCOMUtils.defineLazyGetter(this, "gRadi
           radioInterface.receiveMessage(msg);
         } else {
           // Not really do it but respond success.
           radioInterface.setRadioEnabledResponse(msg.target, msg.json.data);
         }
 
         this._processNextMessage();
       } else {
-        this.request = (function() {
+        _request = function() {
           radioInterface.receiveMessage(msg);
-        }).bind(this);
+        };
 
         // In 2G network, modem takes 35+ seconds to process deactivate data
         // call request if device has active voice call (please see bug 964974
         // for more details). Therefore we should hangup all active voice calls
         // first. And considering some DSDS architecture, toggling one radio may
         // toggle both, so we send hangUpAll to all clients.
-        for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) {
-          let iface = this.ril.getRadioInterface(i);
+        for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
+          let iface = _ril.getRadioInterface(i);
           iface.workerMessenger.send("hangUpAll");
         }
 
         // In some DSDS architecture with only one modem, toggling one radio may
         // toggle both. Therefore, for safely turning off, we should first
         // explicitly deactivate all data calls from all clients.
         this._deactivateDataCalls().then(() => {
-          if (DEBUG) debug("setRadioEnabled: deactivation done");
+          if (DEBUG) debug("RadioControl: deactivation done");
           this._executeRequest();
         });
 
         this._createTimer();
       }
     },
 
     _deactivateDataCalls: function() {
-      if (DEBUG) debug("setRadioEnabled: deactivating data calls...");
-      this.deactivatingDeferred = {};
+      if (DEBUG) debug("RadioControl: deactivating data calls...");
+      _deactivatingDeferred = {};
 
       let promise = Promise.resolve();
-      for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) {
+      for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
         promise = promise.then(this._deactivateDataCallsForClient(i));
       }
 
       return promise;
     },
 
     _deactivateDataCallsForClient: function(clientId) {
-      return (function() {
-        let deferred = this.deactivatingDeferred[clientId] = Promise.defer();
+      return function() {
+        let deferred = _deactivatingDeferred[clientId] = Promise.defer();
         let dataConnectionHandler = gDataConnectionManager.getConnectionHandler(clientId);
         dataConnectionHandler.deactivateDataCalls();
         return deferred.promise;
-      }).bind(this);
+      };
     },
 
     _createTimer: function() {
-      if (!this.timer) {
-        this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      if (_timer) {
+        _timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       }
-      this.timer.initWithCallback(this._executeRequest.bind(this),
-                                  RADIO_POWER_OFF_TIMEOUT,
-                                  Ci.nsITimer.TYPE_ONE_SHOT);
+      _timer.initWithCallback(this._executeRequest.bind(this),
+                              RADIO_POWER_OFF_TIMEOUT,
+                              Ci.nsITimer.TYPE_ONE_SHOT);
     },
 
     _cancelTimer: function() {
-      if (this.timer) {
-        this.timer.cancel();
+      if (_timer) {
+        _timer.cancel();
       }
     },
 
     _executeRequest: function() {
-      if (typeof this.request === "function") {
-        if (DEBUG) debug("setRadioEnabled: executeRequest");
+      if (typeof _request === "function") {
+        if (DEBUG) debug("RadioControl: executeRequest");
         this._cancelTimer();
-        this.request();
-        this.request = null;
+        _request();
+        _request = null;
       }
       this._processNextMessage();
     },
   };
 });
 
 XPCOMUtils.defineLazyGetter(this, "gDataConnectionManager", function () {
   return {
@@ -2150,16 +2174,17 @@ RadioInterface.prototype = {
       case "otastatuschange":
         this.handleOtaStatus(message);
         break;
       case "radiostatechange":
         this.handleRadioStateChange(message);
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
+        gRadioEnabledController.receiveCardState(this.clientId);
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
         let ackOk = this.handleSmsReceived(message);
         // Note: ACK has been done by modem for NEW_SMS_ON_SIM
         if (ackOk && message.simStatus === undefined) {
           this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK });
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2427,16 +2427,17 @@ this.GECKO_RADIOSTATE_OFF           = "o
 this.GECKO_RADIOSTATE_READY         = "ready";
 
 this.GECKO_DETAILED_RADIOSTATE_UNKNOWN    = null;
 this.GECKO_DETAILED_RADIOSTATE_ENABLING   = "enabling";
 this.GECKO_DETAILED_RADIOSTATE_ENABLED    = "enabled";
 this.GECKO_DETAILED_RADIOSTATE_DISABLING  = "disabling";
 this.GECKO_DETAILED_RADIOSTATE_DISABLED   = "disabled";
 
+this.GECKO_CARDSTATE_UNINITIALIZED                 = "uninitialized";
 this.GECKO_CARDSTATE_UNDETECTED                    = null;
 this.GECKO_CARDSTATE_ILLEGAL                       = "illegal";
 this.GECKO_CARDSTATE_UNKNOWN                       = "unknown";
 this.GECKO_CARDSTATE_PIN_REQUIRED                  = "pinRequired";
 this.GECKO_CARDSTATE_PUK_REQUIRED                  = "pukRequired";
 this.GECKO_CARDSTATE_PERSONALIZATION_IN_PROGRESS   = "personalizationInProgress";
 this.GECKO_CARDSTATE_PERSONALIZATION_READY         = "personalizationReady";
 this.GECKO_CARDSTATE_NETWORK_LOCKED                = "networkLocked";
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -275,17 +275,17 @@ let RIL = {
      * ICC status. Keeps a reference of the data response to the
      * getICCStatus request.
      */
     this.iccStatus = null;
 
     /**
      * Card state
      */
-    this.cardState = GECKO_CARDSTATE_UNDETECTED;
+    this.cardState = GECKO_CARDSTATE_UNINITIALIZED;
 
     /**
      * Strings
      */
     this.IMEI = null;
     this.IMEISV = null;
     this.ESN = null;
     this.MEID = null;
@@ -2983,18 +2983,19 @@ let RIL = {
       return;
     }
 
     // fetchICCRecords will need to read aid, so read aid here.
     this.aid = app.aid;
     this.appType = app.app_type;
     this.iccInfo.iccType = GECKO_CARD_TYPE[this.appType];
     // Try to get iccId only when cardState left GECKO_CARDSTATE_UNDETECTED.
-    if (this.cardState === GECKO_CARDSTATE_UNDETECTED &&
-        iccStatus.cardState === CARD_STATE_PRESENT) {
+    if (iccStatus.cardState === CARD_STATE_PRESENT &&
+        (this.cardState === GECKO_CARDSTATE_UNINITIALIZED ||
+         this.cardState === GECKO_CARDSTATE_UNDETECTED)) {
       ICCRecordHelper.readICCID();
     }
 
     switch (app.app_state) {
       case CARD_APPSTATE_ILLEGAL:
         newCardState = GECKO_CARDSTATE_ILLEGAL;
         break;
       case CARD_APPSTATE_PIN: