Bug 736941 - B2G RIL: Fetch SIM record. r=philikon a=b2g-only

This commit is contained in:
Yoshi Huang 2012-04-18 18:07:29 +08:00
parent 77c3cee342
commit 5ce156c88f
3 changed files with 386 additions and 77 deletions

View File

@ -150,7 +150,7 @@ function RadioInterfaceLayer() {
this.radioState = {
radioState: RIL.GECKO_RADIOSTATE_UNAVAILABLE,
cardState: RIL.GECKO_CARDSTATE_UNAVAILABLE,
msisdn: null,
icc: null,
// These objects implement the nsIDOMMozMobileConnectionInfo interface,
// although the actual implementation lives in the content process.
@ -276,8 +276,8 @@ RadioInterfaceLayer.prototype = {
" dst=" + message.dstFlag +
" timestamp=" + message.localTimeStampInMS);
break;
case "siminfo":
this.radioState.msisdn = message.msisdn;
case "iccinfochange":
this.radioState.icc = message;
break;
default:
throw new Error("Don't know about this message type: " + message.type);

View File

@ -373,34 +373,35 @@ const CALL_PRESENTATION_UNKNOWN = 2;
const CALL_PRESENTATION_PAYPHONE = 3;
// 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_READ_BINARY = 0xb0;
const ICC_COMMAND_READ_RECORD = 0xb2;
const ICC_COMMAND_GET_RESPONSE = 0xc0;
const ICC_COMMAND_UPDATE_BINARY = 0xd6;
const ICC_COMMAND_UPDATE_RECORD = 0xdc;
// 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_ICCID = 0x2fe2;
const ICC_EF_IMG = 0x4f20;
const ICC_EF_SST = 0x6f38;
const ICC_EF_UST = 0x6f38; // For USIM
const ICC_EF_ADN = 0x6f3a;
const ICC_EF_FDN = 0x6f3b;
const ICC_EF_SMS = 0x6f3c;
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;
const ICC_EF_SPN = 0x6f46;
const ICC_EF_SDN = 0x6f49;
const ICC_EF_EXT1 = 0x6f4a;
const ICC_EF_EXT2 = 0x6f4b;
const ICC_EF_EXT3 = 0x6f4c;
const ICC_EF_AD = 0x6fad;
const ICC_EF_PNN = 0x6fc5;
const ICC_EF_MBDN = 0x6fc7;
const ICC_EF_EXT6 = 0x6fc8; // Ext record for EF[MBDN]
const ICC_EF_MBI = 0x6fc9;
const ICC_EF_MWIS = 0x6fca;
const ICC_EF_CFIS = 0x6fcb;
const ICC_EF_SPDI = 0x6fcd;
// Types of files TS 11.11 9.3
const TYPE_RFU = 0;
@ -408,6 +409,7 @@ const TYPE_MF = 1;
const TYPE_DF = 2;
const TYPE_EF = 4;
const RESPONSE_DATA_FILE_SIZE = 2;
const RESPONSE_DATA_FILE_ID_1 = 4;
const RESPONSE_DATA_FILE_ID_2 = 5;
const RESPONSE_DATA_FILE_TYPE = 6;
@ -425,9 +427,9 @@ 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;
// For retrieving MSISDN, TS 151.011 clause 10.5.5
const MSISDN_FOOTER_SIZE_BYTES = 14;
const MSISDN_MAX_NUMBER_SIZE_BYTES = 10;
// READ_RECORD mode, TS 102.221
const READ_RECORD_ABSOLUTE_MODE = 4;
@ -439,6 +441,7 @@ const GET_RESPONSE_EF_SIZE_BYTES = 15;
// EF path
const EF_PATH_MF_SIM = "3f00";
const EF_PATH_DF_TELECOM = "7f10";
const EF_PATH_DF_GSM = "7f20";
// Status code of sw1 for ICC I/O,
// see GSM11.11 and TS 51.011 clause 9.4, and ISO 7816-4

View File

@ -75,6 +75,8 @@ const UINT16_SIZE = 2;
const UINT32_SIZE = 4;
const PARCEL_SIZE_SIZE = UINT32_SIZE;
const PDU_HEX_OCTET_SIZE = 4;
let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = false;
let RILQUIRKS_DATACALLSTATE_DOWN_IS_UP = false;
// This flag defaults to true since on RIL v6 and later, we get the
@ -202,6 +204,51 @@ let Buf = {
* These are all little endian, apart from readParcelSize();
*/
/**
* Ensure position specified is readable.
*
* @param index
* Data position in incoming parcel, valid from 0 to
* this.currentParcelSize.
*/
ensureIncomingAvailable: function ensureIncomingAvailable(index) {
if (index >= this.currentParcelSize) {
throw new Error("Trying to read data beyond the parcel end!");
} else if (index < 0) {
throw new Error("Trying to read data before the parcel begin!");
}
},
/**
* Seek in current incoming parcel.
*
* @param offset
* Seek offset in relative to current position.
*/
seekIncoming: function seekIncoming(offset) {
// Translate to 0..currentParcelSize
let cur = this.currentParcelSize - this.readAvailable;
let newIndex = cur + offset;
this.ensureIncomingAvailable(newIndex);
// ... incomingReadIndex -->|
// 0 new cur currentParcelSize
// |================|=======|===================|
// |<-- cur -->|<- readAvailable ->|
// |<-- newIndex -->|<-- new readAvailable -->|
this.readAvailable = this.currentParcelSize - newIndex;
// Translate back:
if (this.incomingReadIndex < cur) {
// The incomingReadIndex is wrapped.
newIndex += this.INCOMING_BUFFER_LENGTH;
}
newIndex += (this.incomingReadIndex - cur);
newIndex %= this.INCOMING_BUFFER_LENGTH;
this.incomingReadIndex = newIndex;
},
readUint8Unchecked: function readUint8Unchecked() {
let value = this.incomingBytes[this.incomingReadIndex];
this.incomingReadIndex = (this.incomingReadIndex + 1) %
@ -210,9 +257,9 @@ let Buf = {
},
readUint8: function readUint8() {
if (!this.readAvailable) {
throw new Error("Trying to read data beyond the parcel end!");
}
// Translate to 0..currentParcelSize
let cur = this.currentParcelSize - this.readAvailable;
this.ensureIncomingAvailable(cur);
this.readAvailable--;
return this.readUint8Unchecked();
@ -567,9 +614,12 @@ let RIL = {
*/
IMEI: null,
IMEISV: null,
IMSI: null,
SMSC: null,
MSISDN: null,
/**
* ICC information, such as MSISDN, IMSI, ...etc.
*/
iccInfo: {},
voiceRegistrationState: {},
dataRegistrationState: {},
@ -766,10 +816,10 @@ let RIL = {
*
* @param command
* The I/O command, one of the ICC_COMMAND_* constants.
* @param fileid
* @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 pathId
* String type, check the 'pathid' parameter from TS 27.007 +CRSM.
* @param p1, p2, p3
* Arbitrary integer parameters for the command.
* @param data
@ -780,8 +830,8 @@ let RIL = {
iccIO: function iccIO(options) {
let token = Buf.newParcel(REQUEST_SIM_IO, options);
Buf.writeUint32(options.command);
Buf.writeUint32(options.fileid);
Buf.writeString(options.pathid);
Buf.writeUint32(options.fileId);
Buf.writeString(options.pathId);
Buf.writeUint32(options.p1);
Buf.writeUint32(options.p2);
Buf.writeUint32(options.p3);
@ -792,19 +842,183 @@ let RIL = {
Buf.sendParcel();
},
/**
* Fetch ICC records.
*/
fetchICCRecords: function fetchICCRecords() {
this.getIMSI();
this.getMSISDN();
this.getAD();
this.getUST();
},
/**
* Update the ICC information to RadioInterfaceLayer.
*/
_handleICCInfoChange: function _handleICCInfoChange() {
this.iccInfo.type = "iccinfochange";
this.sendDOMMessage(this.iccInfo);
},
getIMSI: function getIMSI() {
Buf.simpleRequest(REQUEST_GET_IMSI);
},
/**
* Read the MSISDN from the ICC.
*/
getMSISDN: function getMSISDN() {
function callback() {
let length = Buf.readUint32();
// Each octet is encoded into two chars.
let recordLength = length / 2;
// Skip prefixed alpha identifier
Buf.seekIncoming((recordLength - MSISDN_FOOTER_SIZE_BYTES) *
PDU_HEX_OCTET_SIZE);
// Dialling Number/SSC String
let len = GsmPDUHelper.readHexOctet();
if (len > MSISDN_MAX_NUMBER_SIZE_BYTES) {
debug("ICC_EF_MSISDN: invalid length of BCD number/SSC contents - " + len);
return;
}
this.iccInfo.MSISDN = GsmPDUHelper.readAddress(len);
let delimiter = Buf.readUint16();
if (!(length & 1)) {
delimiter |= Buf.readUint16();
}
if (DEBUG) {
if (delimiter != 0) {
debug("Something's wrong, found string delimiter: " + delimiter);
}
}
if (DEBUG) debug("MSISDN: " + this.iccInfo.MSISDN);
if (this.iccInfo.MSISDN) {
this._handleICCInfoChange();
}
}
this.iccIO({
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,
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,
type: EF_TYPE_LINEAR_FIXED,
callback: callback,
});
},
/**
* Read the AD from the ICC.
*/
getAD: function getAD() {
function callback() {
let length = Buf.readUint32();
// Each octet is encoded into two chars.
let len = length / 2;
this.iccInfo.AD = GsmPDUHelper.readHexOctetArray(len);
let delimiter = Buf.readUint16();
if (!(length & 1)) {
delimiter |= Buf.readUint16();
}
if (DEBUG) {
if (delimiter != 0) {
debug("Something's wrong, found string delimiter: " + delimiter);
}
}
if (DEBUG) {
let str = "";
for (let i = 0; i < this.iccInfo.AD.length; i++) {
str += this.iccInfo.AD[i] + ", ";
}
debug("AD: " + str);
}
if (this.iccInfo.IMSI) {
// MCC is the first 3 digits of IMSI
this.iccInfo.MCC = this.iccInfo.IMSI.substr(0,3);
// The 4th byte of the response is the length of MNC
this.iccInfo.MNC = this.iccInfo.IMSI.substr(3, this.iccInfo.AD[3]);
if (DEBUG) debug("MCC: " + this.iccInfo.MCC + " MNC: " + this.iccInfo.MNC);
this._handleICCInfoChange();
}
}
this.iccIO({
command: ICC_COMMAND_GET_RESPONSE,
fileId: ICC_EF_AD,
pathId: EF_PATH_MF_SIM + EF_PATH_DF_GSM,
p1: 0, // For GET_RESPONSE, p1 = 0
p2: 0, // For GET_RESPONSE, p2 = 0
p3: GET_RESPONSE_EF_SIZE_BYTES,
data: null,
pin2: null,
type: EF_TYPE_TRANSPARENT,
callback: callback,
});
},
/**
* Get whether specificed USIM service is available.
*
* @param service
* Service id, valid in 1..N. See 3GPP TS 31.102 4.2.8.
* @return
* true if the service is enabled,
* false otherwise.
*/
isUSTServiceAvailable: function isUSTServiceAvailable(service) {
service -= 1;
let index = service / 8;
let bitmask = 1 << (service % 8);
return this.UST && (index < this.UST.length) && (this.UST[index] & bitmask);
},
/**
* Read the UST from the ICC.
*/
getUST: function getUST() {
function callback() {
let length = Buf.readUint32();
// Each octet is encoded into two chars.
let len = length / 2;
this.iccInfo.UST = GsmPDUHelper.readHexOctetArray(len);
let delimiter = Buf.readUint16();
if (!(length & 1)) {
delimiter |= Buf.readUint16();
}
if (DEBUG) {
if (delimiter != 0) {
debug("Something's wrong, found string delimiter: " + delimiter);
}
}
if (DEBUG) {
let str = "";
for (let i = 0; i < this.iccInfo.UST.length; i++) {
str += this.iccInfo.UST[i] + ", ";
}
debug("UST: " + str);
}
}
this.iccIO({
command: ICC_COMMAND_GET_RESPONSE,
fileId: ICC_EF_UST,
pathId: EF_PATH_MF_SIM + EF_PATH_DF_GSM,
p1: 0, // For GET_RESPONSE, p1 = 0
p2: 0, // For GET_RESPONSE, p2 = 0
p3: GET_RESPONSE_EF_SIZE_BYTES,
data: null,
pin2: null,
type: EF_TYPE_TRANSPARENT,
callback: callback,
});
},
@ -1263,7 +1477,7 @@ let RIL = {
case CARD_APPSTATE_READY:
this.requestNetworkInfo();
this.getSignalStrength();
this.getMSISDN();
this.fetchICCRecords();
newCardState = GECKO_CARDSTATE_READY;
break;
case CARD_APPSTATE_UNKNOWN:
@ -1281,33 +1495,119 @@ let RIL = {
},
/**
* Process the MSISDN ICC I/O response.
* Process a ICC_COMMAND_GET_RESPONSE type command for REQUEST_SIM_IO.
*/
_processMSISDNResponse: function _processMSISDNResponse(options) {
_processICCIOGetResponse: function _processICCIOGetResponse(options) {
let length = Buf.readUint32();
// The format is from TS 51.011, clause 9.2.1
// Skip RFU, data[0] data[1]
Buf.seekIncoming(2 * PDU_HEX_OCTET_SIZE);
// File size, data[2], data[3]
let fileSize = (GsmPDUHelper.readHexOctet() << 8) |
GsmPDUHelper.readHexOctet();
// 2 bytes File id. data[4], data[5]
let fileId = (GsmPDUHelper.readHexOctet() << 8) |
GsmPDUHelper.readHexOctet();
if (fileId != options.fileId) {
if (DEBUG) {
debug("Expected file ID " + options.fileId + " but read " + fileId);
}
return;
}
// Type of file, data[6]
let fileType = GsmPDUHelper.readHexOctet();
if (fileType != TYPE_EF) {
if (DEBUG) {
debug("Unexpected file type " + fileType);
}
return;
}
// Skip 1 byte RFU, data[7],
// 3 bytes Access conditions, data[8] data[9] data[10],
// 1 byte File status, data[11],
// 1 byte Length of the following data, data[12].
Buf.seekIncoming(((RESPONSE_DATA_STRUCTURE - RESPONSE_DATA_FILE_TYPE - 1) *
PDU_HEX_OCTET_SIZE));
// Read Structure of EF, data[13]
let efType = GsmPDUHelper.readHexOctet();
if (efType != options.type) {
if (DEBUG) {
debug("Expected EF type " + options.type + " but read " + efType);
}
return;
}
// Length of a record, data[14]
let recordSize = GsmPDUHelper.readHexOctet();
let delimiter = Buf.readUint16();
if (!(length & 1)) {
delimiter |= Buf.readUint16();
}
if (DEBUG) {
if (delimiter != 0) {
debug("Something's wrong, found string delimiter: " + delimiter);
}
}
switch (options.type) {
case EF_TYPE_LINEAR_FIXED:
// Reuse the options object and update some properties.
options.command = ICC_COMMAND_READ_RECORD;
options.p1 = 1; // Record number, always use the 1st record
options.p2 = READ_RECORD_ABSOLUTE_MODE;
options.p3 = recordSize;
this.iccIO(options);
break;
case EF_TYPE_TRANSPARENT:
// Reuse the options object and update some properties.
options.command = ICC_COMMAND_READ_BINARY;
options.p3 = fileSize;
this.iccIO(options);
break;
}
},
/**
* Process a ICC_COMMAND_READ_RECORD type command for REQUEST_SIM_IO.
*/
_processICCIOReadRecord: function _processICCIOReadRecord(options) {
if (options.callback) {
options.callback.call(this);
}
},
/**
* Process a ICC_COMMAND_READ_BINARY type command for REQUEST_SIM_IO.
*/
_processICCIOReadBinary: function _processICCIOReadBinary(options) {
if (options.callback) {
options.callback.call(this);
}
},
/**
* Process ICC I/O response.
*/
_processICCIO: function _processICCIO(options) {
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 options = {
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,
};
this.iccIO(options);
this._processICCIOGetResponse(options);
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 || null;
this.sendDOMMessage({type: "siminfo", msisdn: this.MSISDN});
this._processICCIOReadRecord(options);
break;
case ICC_COMMAND_READ_BINARY:
this._processICCIOReadBinary(options);
break;
}
},
@ -1869,7 +2169,7 @@ RIL[REQUEST_GET_IMSI] = function REQUEST_GET_IMSI(length, options) {
return;
}
this.IMSI = Buf.readString();
this.iccInfo.IMSI = Buf.readString();
};
RIL[REQUEST_HANGUP] = function REQUEST_HANGUP(length, options) {
if (options.rilRequestError) {
@ -2106,18 +2406,13 @@ RIL[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) {
// See GSM11.11, TS 51.011 clause 9.4, and ISO 7816-4 for the error
// description.
if (DEBUG) {
debug("ICC I/O Error EF id = " + options.fileid.toString(16) +
debug("ICC I/O Error EF id = " + options.fileId.toString(16) +
" command = " + options.command.toString(16) +
"(" + sw1.toString(16) + "/" + sw2.toString(16) + ")");
}
return;
}
switch (options.fileid) {
case ICC_EF_MSISDN:
this._processMSISDNResponse(options);
break;
}
this._processICCIO(options);
};
RIL[REQUEST_SEND_USSD] = null;
RIL[REQUEST_CANCEL_USSD] = null;
@ -2559,6 +2854,17 @@ let GsmPDUHelper = {
this.writeHexNibble(octet);
},
/**
* Read an array of hex-encoded octets.
*/
readHexOctetArray: function readHexOctetArray(length) {
let array = new Uint8Array(length);
for (let i = 0; i < length; i++) {
array[i] = this.readHexOctet();
}
return array;
},
/**
* Convert an octet (number) to a BCD number.
*