Bug 720638 - B2G RIL: Read MSISDN. r=philikon
authoryoshi huang <yhuang@mozilla.com>
Wed, 07 Mar 2012 14:14:37 -0800
changeset 88431 36c4724de5bf02ab2a2acbbd66f980d4c3cd2e34
parent 88430 96c36ac5ad2bc7265504ce1d8b0b56bbb0719394
child 88432 a4415019a57ad50b54e1d3865f21e2ee4db6ffa4
push id22197
push userpweitershausen@mozilla.com
push dateWed, 07 Mar 2012 22:44:33 +0000
treeherdermozilla-central@453d5c733caa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilikon
bugs720638
milestone13.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 720638 - B2G RIL: Read MSISDN. r=philikon
dom/system/b2g/ril_consts.js
dom/system/b2g/ril_worker.js
--- a/dom/system/b2g/ril_consts.js
+++ b/dom/system/b2g/ril_consts.js
@@ -255,16 +255,90 @@ const TOA_UNKNOWN = 0x81;
 
 const CALL_PRESENTATION_ALLOWED = 0;
 const CALL_PRESENTATION_RESTRICTED = 1;
 const CALL_PRESENTATION_UNKNOWN = 2;
 const CALL_PRESENTATION_PAYPHONE = 3;
 
 const SMS_HANDLED = 0;
 
+// ICC commands, see TS 27.007 +CRSM commands
+const ICC_COMMAND_READ_BINARY = 0xb0;
+const ICC_COMMAND_UPDATE_BINARY = 0xd6;
+const ICC_COMMAND_READ_RECORD = 0xb2;
+const ICC_COMMAND_UPDATE_RECORD = 0xdc;
+const ICC_COMMAND_SEEK = 0xa2;
+const ICC_COMMAND_GET_RESPONSE = 0xc0;
+
+// ICC constants, GSM SIM file ids from TS 51.011
+const ICC_EF_ADN = 0x6F3A;
+const ICC_EF_FDN = 0x6F3B;
+const ICC_EF_SDN = 0x6F49;
+const ICC_EF_EXT1 = 0x6F4A;
+const ICC_EF_EXT2 = 0x6F4B;
+const ICC_EF_EXT3 = 0x6F4C;
+const ICC_EF_EXT6 = 0x6fc8;   // Ext record for EF[MBDN]
+const ICC_EF_MWIS = 0x6FCA;
+const ICC_EF_MBDN = 0x6fc7;
+const ICC_EF_PNN = 0x6fc5;
+const ICC_EF_SPN = 0x6F46;
+const ICC_EF_SMS = 0x6F3C;
+const ICC_EF_ICCID = 0x2fe2;
+const ICC_EF_AD = 0x6FAD;
+const ICC_EF_MBI = 0x6fc9;
+const ICC_EF_MSISDN = 0x6f40;
+const ICC_EF_SPDI = 0x6fcd;
+const ICC_EF_SST = 0x6f38;
+const ICC_EF_CFIS = 0x6FCB;
+const ICC_EF_IMG = 0x4f20;
+
+// Types of files  TS 11.11 9.3
+const TYPE_RFU = 0;
+const TYPE_MF  = 1;
+const TYPE_DF  = 2;
+const TYPE_EF  = 4;
+
+const RESPONSE_DATA_FILE_ID_1 = 4;
+const RESPONSE_DATA_FILE_ID_2 = 5;
+const RESPONSE_DATA_FILE_TYPE = 6;
+const RESPONSE_DATA_RFU_3 = 7;
+const RESPONSE_DATA_ACCESS_CONDITION_1 = 8;
+const RESPONSE_DATA_ACCESS_CONDITION_2 = 9;
+const RESPONSE_DATA_ACCESS_CONDITION_3 = 10;
+const RESPONSE_DATA_FILE_STATUS = 11;
+const RESPONSE_DATA_LENGTH = 12;
+const RESPONSE_DATA_STRUCTURE = 13;
+const RESPONSE_DATA_RECORD_LENGTH = 14;
+
+// Types of files  TS 11.11 9.3
+const EF_TYPE_TRANSPARENT = 0;
+const EF_TYPE_LINEAR_FIXED = 1;
+const EF_TYPE_CYCLIC = 3;
+
+// For retriveing MSISDN
+const FOOTER_SIZE_BYTES = 14;
+const MAX_NUMBER_SIZE_BYTES = 11;
+
+// READ_RECORD mode,  TS 102.221
+const READ_RECORD_ABSOLUTE_MODE = 4;
+
+// GET_RESPONSE mandatory response size for EF, see TS 51.011 clause 9, 
+// 'Response data in case of an EF.'
+const GET_RESPONSE_EF_SIZE_BYTES = 15;
+
+// EF path
+const EF_PATH_MF_SIM = "3f00";
+const EF_PATH_DF_TELECOM = "7f10";
+
+// Status code for ICC I/O, 
+// see GSM11.11 and TS 51.011 clause 9.4.
+const STATUS_NORMAL_ENDING = 0x90;
+const STATUS_NORMAL_ENDING_WITH_EXTRA = 0x91;
+const STATUS_WITH_SIM_DATA = 0x9e;
+const STATUS_WITH_RESPONSE_DATA = 0x9f;
 
 /**
  * GSM PDU constants
  */
 
 // PDU TYPE-OF-ADDRESS
 const PDU_TOA_UNKNOWN       = 0x80; // Unknown. This is used when the user or
                                     // network has no a priori information
--- a/dom/system/b2g/ril_worker.js
+++ b/dom/system/b2g/ril_worker.js
@@ -907,16 +907,51 @@ let RIL = {
     Buf.writeString(user);
     Buf.writeString(passwd);
     Buf.writeString(chappap.toString());
     Buf.writeString(pdptype);
     Buf.sendParcel();
     return token;
   },
 
+   /**
+   *  Request an ICC I/O operation.
+   * 
+   *  See TS 27.007 "restricted SIM" operation, "AT Command +CRSM".
+   *  The sequence is in the same order as how libril reads this parcel,
+   *  see the struct RIL_SIM_IO_v5 or RIL_SIM_IO_v6 defined in ril.h
+   *
+   *  @param command 
+   *         The I/O command, one of the ICC_COMMAND_* constants.
+   *  @param fileid
+   *         The file to operate on, one of the ICC_EF_* constants.
+   *  @param pathid
+   *         String type, check pathid from TS 27.007 +CRSM  
+   *  @param p1, p2, p3
+   *         Arbitrary integer parameters for the command.
+   *  @param data
+   *         String parameter for the command.
+   *  @param pin2 [optional]
+   *         String containing the PIN2.
+   */
+  iccIO: function iccIO (options) {
+    let token = Buf.newParcel(REQUEST_SIM_IO, options);
+    Buf.writeUint32(options.command);
+    Buf.writeUint32(options.fileid);
+    Buf.writeString(options.path);
+    Buf.writeUint32(options.p1);
+    Buf.writeUint32(options.p2);
+    Buf.writeUint32(options.p3);
+    Buf.writeString(options.data);
+    if (request.pin2 != null) {
+      Buf.writeString(pin2);
+    }
+    Buf.sendParcel();
+  },
+  
   /**
    * Deactivate a data call.
    *
    * @param cid
    *        String containing CID.
    * @param reason
    *        One of DATACALL_DEACTIVATE_* constants.
    */
@@ -1120,17 +1155,19 @@ RIL[REQUEST_SEND_SMS] = function REQUEST
   options.errorCode = Buf.readUint32();
   Phone.onSendSMS(options);
 };
 RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null;
 RIL[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL() {
   let [cid, ifname, ipaddr, dns, gw] = Buf.readStringList();
   Phone.onSetupDataCall(Buf.lastSolicitedToken, cid, ifname, ipaddr, dns, gw);
 };
-RIL[REQUEST_SIM_IO] = null;
+RIL[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) {
+  Phone.onICCIO(options);
+};
 RIL[REQUEST_SEND_USSD] = null;
 RIL[REQUEST_CANCEL_USSD] = null;
 RIL[REQUEST_GET_CLIR] = null;
 RIL[REQUEST_SET_CLIR] = null;
 RIL[REQUEST_QUERY_CALL_FORWARD_STATUS] = null;
 RIL[REQUEST_SET_CALL_FORWARD] = null;
 RIL[REQUEST_QUERY_CALL_WAITING] = null;
 RIL[REQUEST_SET_CALL_WAITING] = null;
@@ -1333,16 +1370,17 @@ let Phone = {
 
   /**
    * Strings
    */
   IMEI: null,
   IMEISV: null,
   IMSI: null,
   SMSC: null,
+  MSISDN: null,
 
   registrationState: {},
   gprsRegistrationState: {},
 
   /**
    * List of strings identifying the network operator.
    */
   operator: null,
@@ -1479,16 +1517,17 @@ let Phone = {
     if (newState == RADIO_STATE_SIM_READY  ||
         newState == RADIO_STATE_RUIM_READY ||
         newState == RADIO_STATE_NV_READY) {
       // The ICC has become available. Get all the things.
       RIL.getICCStatus();
       this.requestNetworkInfo();
       RIL.getSignalStrength();
       RIL.getSMSCAddress();
+      this.getMSISDN();
       this.sendDOMMessage({type: "cardstatechange",
                            cardState: GECKO_CARDSTATE_READY});
     }
     if (newState == RADIO_STATE_SIM_LOCKED_OR_ABSENT  ||
         newState == RADIO_STATE_RUIM_LOCKED_OR_ABSENT) {
       RIL.getICCStatus();
       this.sendDOMMessage({type: "cardstatechange",
                            cardState: GECKO_CARDSTATE_UNAVAILABLE});
@@ -1682,16 +1721,62 @@ let Phone = {
   onIMEI: function onIMEI(imei) {
     this.IMEI = imei;
   },
 
   onIMEISV: function onIMEISV(imeiSV) {
     this.IMEISV = imeiSV;
   },
 
+  onICCIO: function onICCIO(options) {
+    switch (options.fileid) {
+      case ICC_EF_MSISDN:
+        this.readMSISDNResponse(options);
+        break;
+    }
+  },
+  
+  readMSISDNResponse: function readMSISDNResponse(options) {
+    let sw1 = Buf.readUint32();
+    let sw2 = Buf.readUint32();
+    // See GSM11.11 section 9.4 for sw1 and sw2
+    if (sw1 != STATUS_NORMAL_ENDING) {
+      // TODO: error 
+      // Wait for fix for Bug 713451 to report error.
+      debug("Error in iccIO");
+    }
+    if (DEBUG) debug("ICC I/O (" + sw1 + "/" + sw2 + ")");
+
+    switch (options.command) {
+      case ICC_COMMAND_GET_RESPONSE:
+        let response = Buf.readString();
+        let recordSize = parseInt(
+            response.substr(RESPONSE_DATA_RECORD_LENGTH * 2, 2), 16) & 0xff;
+        let request = {
+          command: ICC_COMMAND_READ_RECORD,
+          fileid:  ICC_EF_MSISDN,
+          pathid:  EF_PATH_MF_SIM + EF_PATH_DF_TELECOM,
+          p1:      1, // Record number, MSISDN is always in the 1st record
+          p2:      READ_RECORD_ABSOLUTE_MODE,
+          p3:      recordSize,
+          data:    null,
+          pin2:    null,
+        };
+        RIL.iccIO(request);
+        break;
+
+      case ICC_COMMAND_READ_RECORD:
+        // Ignore 2 bytes prefix, which is 4 chars
+        let number = GsmPDUHelper.readStringAsBCD().toString().substr(4); 
+        if (DEBUG) debug("MSISDN: " + number);
+        this.MSISDN = number;
+        break;
+    } 
+  },
+
   onRegistrationState: function onRegistrationState(state) {
     let rs = this.registrationState;
     let stateChanged = false;
 
     let regState = RIL.parseInt(state[0], NETWORK_CREG_STATE_UNKNOWN);
     if (rs.regState != regState) {
       rs.regState = regState;
       stateChanged = true;
@@ -2138,16 +2223,33 @@ let Phone = {
   /**
    * Get failure cause code for the last failed PDP context.
    */
   getFailCauseCode: function getFailCauseCode(options) {
     RIL.getFailCauseCode();
   },
 
   /**
+   *  Get MSISDN
+   */ 
+  getMSISDN: function getMSISDN() {
+    let request = {
+      command: ICC_COMMAND_GET_RESPONSE,
+      fileid:  ICC_EF_MSISDN,
+      pathid:  EF_PATH_MF_SIM + EF_PATH_DF_TELECOM,
+      p1:      0, // For GET_RESPONSE, p1 = 0
+      p2:      0, // For GET_RESPONSE, p2 = 0
+      p3:      GET_RESPONSE_EF_SIZE_BYTES,
+      data:    null,
+      pin2:    null,
+    };
+    RIL.iccIO(request);
+  },
+
+  /**
    * Handle incoming messages from the main UI thread.
    *
    * @param message
    *        Object containing the message. Messages are supposed
    */
   handleDOMMessage: function handleMessage(message) {
     if (DEBUG) debug("Received DOM message " + JSON.stringify(message));
     let method = this[message.type];
@@ -2265,30 +2367,47 @@ let GsmPDUHelper = {
    *        Number of nibble *pairs* to read.
    *
    * @return the decimal as a number.
    */
   readSwappedNibbleBCD: function readSwappedNibbleBCD(length) {
     let number = 0;
     for (let i = 0; i < length; i++) {
       let octet = this.readHexOctet();
+      if (octet == 0xff)
+        continue;
       // If the first nibble is an "F" , only the second nibble is to be taken
       // into account.
       if ((octet & 0xf0) == 0xf0) {
         number *= 10;
         number += octet & 0x0f;
         continue;
       }
       number *= 100;
       number += this.octetToBCD(octet);
     }
     return number;
   },
 
   /**
+   *  Read a string from Buf and convert it to BCD
+   * 
+   *  @return the decimal as a number.
+   */ 
+  readStringAsBCD: function readStringAsBCD() {
+    let length = Buf.readUint32();
+    let bcd = this.readSwappedNibbleBCD(length / 2);
+    let delimiter = Buf.readUint16();
+    if (!(length & 1)) {
+      delimiter |= Buf.readUint16();
+    }
+    return bcd;
+  },
+
+  /**
    * Write numerical data as swapped nibble BCD.
    *
    * @param data
    *        Data to write (as a string or a number)
    */
   writeSwappedNibbleBCD: function writeSwappedNibbleBCD(data) {
     data = data.toString();
     if (data.length % 2) {