mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
438 lines
13 KiB
JavaScript
438 lines
13 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
var RIL = {};
|
|
Cu.import("resource://gre/modules/ril_consts.js", RIL);
|
|
|
|
const GONK_ICCSERVICE_CONTRACTID = "@mozilla.org/icc/gonkiccservice;1";
|
|
const GONK_ICCSERVICE_CID = Components.ID("{df854256-9554-11e4-a16c-c39e8d106c26}");
|
|
|
|
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
|
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
|
|
|
const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
|
|
const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
|
|
"@mozilla.org/ril;1",
|
|
"nsIRadioInterfaceLayer");
|
|
|
|
let DEBUG = RIL.DEBUG_RIL;
|
|
function debug(s) {
|
|
dump("IccService: " + s);
|
|
}
|
|
|
|
function IccInfo() {}
|
|
IccInfo.prototype = {
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
|
|
|
|
// nsIIccInfo
|
|
|
|
iccType: null,
|
|
iccid: null,
|
|
mcc: null,
|
|
mnc: null,
|
|
spn: null,
|
|
isDisplayNetworkNameRequired: false,
|
|
isDisplaySpnRequired: false
|
|
};
|
|
|
|
function GsmIccInfo() {}
|
|
GsmIccInfo.prototype = {
|
|
__proto__: IccInfo.prototype,
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
|
|
Ci.nsIIccInfo]),
|
|
|
|
// nsIGsmIccInfo
|
|
|
|
msisdn: null
|
|
};
|
|
|
|
function CdmaIccInfo() {}
|
|
CdmaIccInfo.prototype = {
|
|
__proto__: IccInfo.prototype,
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
|
|
Ci.nsIIccInfo]),
|
|
|
|
// nsICdmaIccInfo
|
|
|
|
mdn: null,
|
|
prlVersion: 0
|
|
};
|
|
|
|
function IccService() {
|
|
this._iccs = [];
|
|
|
|
let numClients = gRadioInterfaceLayer.numRadioInterfaces;
|
|
for (let i = 0; i < numClients; i++) {
|
|
this._iccs.push(new Icc(gRadioInterfaceLayer.getRadioInterface(i)));
|
|
}
|
|
|
|
this._updateDebugFlag();
|
|
|
|
Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
|
|
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
}
|
|
IccService.prototype = {
|
|
classID: GONK_ICCSERVICE_CID,
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: GONK_ICCSERVICE_CID,
|
|
contractID: GONK_ICCSERVICE_CONTRACTID,
|
|
classDescription: "IccService",
|
|
interfaces: [Ci.nsIIccService,
|
|
Ci.nsIGonkIccService],
|
|
flags: Ci.nsIClassInfo.SINGLETON}),
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccService,
|
|
Ci.nsIGonkIccService,
|
|
Ci.nsIObserver]),
|
|
|
|
// An array of Icc instances.
|
|
_iccs: null,
|
|
|
|
_updateDebugFlag: function() {
|
|
try {
|
|
DEBUG = DEBUG ||
|
|
Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
|
|
} catch (e) {}
|
|
},
|
|
|
|
/**
|
|
* nsIIccService interface.
|
|
*/
|
|
getIccByServiceId: function(aServiceId) {
|
|
let icc = this._iccs[aServiceId];
|
|
if (!icc) {
|
|
throw Cr.NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
return icc;
|
|
},
|
|
|
|
/**
|
|
* nsIGonkIccService interface.
|
|
*/
|
|
notifyCardStateChanged: function(aServiceId, aCardState) {
|
|
if (DEBUG) {
|
|
debug("notifyCardStateChanged for service Id: " + aServiceId +
|
|
", CardState: " + aCardState);
|
|
}
|
|
|
|
this.getIccByServiceId(aServiceId)._updateCardState(aCardState);
|
|
},
|
|
|
|
notifyIccInfoChanged: function(aServiceId, aIccInfo) {
|
|
if (DEBUG) {
|
|
debug("notifyIccInfoChanged for service Id: " + aServiceId +
|
|
", IccInfo: " + JSON.stringify(aIccInfo));
|
|
}
|
|
|
|
this.getIccByServiceId(aServiceId)._updateIccInfo(aIccInfo);
|
|
},
|
|
|
|
notifyImsiChanged: function(aServiceId, aImsi) {
|
|
if (DEBUG) {
|
|
debug("notifyImsiChanged for service Id: " + aServiceId +
|
|
", Imsi: " + aImsi);
|
|
}
|
|
|
|
let icc = this.getIccByServiceId(aServiceId);
|
|
icc._imsi = aImsi;
|
|
},
|
|
|
|
/**
|
|
* nsIObserver interface.
|
|
*/
|
|
observe: function(aSubject, aTopic, aData) {
|
|
switch (aTopic) {
|
|
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
|
|
if (aData === kPrefRilDebuggingEnabled) {
|
|
this._updateDebugFlag();
|
|
}
|
|
break;
|
|
case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
|
|
Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
|
|
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
function Icc(aRadioInterface) {
|
|
this._radioInterface = aRadioInterface;
|
|
this._listeners = [];
|
|
}
|
|
Icc.prototype = {
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
|
|
|
|
_radioInterface: null,
|
|
_imsi: null,
|
|
_listeners: null,
|
|
|
|
_updateCardState: function(aCardState) {
|
|
if (this.cardState != aCardState) {
|
|
this.cardState = aCardState;
|
|
}
|
|
|
|
this._deliverListenerEvent("notifyCardStateChanged");
|
|
},
|
|
|
|
// An utility function to copy objects.
|
|
_updateInfo: function(aSrcInfo, aDestInfo) {
|
|
for (let key in aSrcInfo) {
|
|
aDestInfo[key] = aSrcInfo[key];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* We need to consider below cases when update iccInfo:
|
|
* 1. Should clear iccInfo to null if there is no card detected.
|
|
* 2. Need to create corresponding object based on iccType.
|
|
*/
|
|
_updateIccInfo: function(aIccInfo) {
|
|
// Card is not detected, clear iccInfo to null.
|
|
if (!aIccInfo || !aIccInfo.iccid) {
|
|
if (this.iccInfo) {
|
|
if (DEBUG) {
|
|
debug("Card is not detected, clear iccInfo to null.");
|
|
}
|
|
this.iccInfo = null;
|
|
this._deliverListenerEvent("notifyIccInfoChanged");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If iccInfo is null, new corresponding object based on iccType.
|
|
if (!this.iccInfo ||
|
|
this.iccInfo.iccType != aIccInfo.iccType) {
|
|
if (aIccInfo.iccType === "ruim" || aIccInfo.iccType === "csim") {
|
|
this.iccInfo = new CdmaIccInfo();
|
|
} else if (aIccInfo.iccType === "sim" || aIccInfo.iccType === "usim") {
|
|
this.iccInfo = new GsmIccInfo();
|
|
} else {
|
|
this.iccInfo = new IccInfo();
|
|
}
|
|
}
|
|
|
|
this._updateInfo(aIccInfo, this.iccInfo);
|
|
|
|
this._deliverListenerEvent("notifyIccInfoChanged");
|
|
|
|
// Update lastKnownSimMcc.
|
|
if (aIccInfo.mcc) {
|
|
try {
|
|
Services.prefs.setCharPref("ril.lastKnownSimMcc",
|
|
aIccInfo.mcc.toString());
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
|
|
_deliverListenerEvent: function(aName, aArgs) {
|
|
let listeners = this._listeners.slice();
|
|
for (let listener of listeners) {
|
|
if (this._listeners.indexOf(listener) === -1) {
|
|
continue;
|
|
}
|
|
let handler = listener[aName];
|
|
if (typeof handler != "function") {
|
|
throw new Error("No handler for " + aName);
|
|
}
|
|
try {
|
|
handler.apply(listener, aArgs);
|
|
} catch (e) {
|
|
if (DEBUG) {
|
|
debug("listener for " + aName + " threw an exception: " + e);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_modifyCardLock: function(aOperation, aOptions, aCallback) {
|
|
this._radioInterface.sendWorkerMessage(aOperation,
|
|
aOptions,
|
|
(aResponse) => {
|
|
if (aResponse.errorMsg) {
|
|
let retryCount =
|
|
(aResponse.retryCount !== undefined) ? aResponse.retryCount : -1;
|
|
aCallback.notifyCardLockError(aResponse.errorMsg, retryCount);
|
|
return;
|
|
}
|
|
|
|
aCallback.notifySuccess();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Helper to match the MVNO pattern with IMSI.
|
|
*
|
|
* Note: Characters 'x' and 'X' in MVNO are skipped and not compared.
|
|
* E.g., if the aMvnoData passed is '310260x10xxxxxx', then the
|
|
* function returns true only if imsi has the same first 6 digits, 8th
|
|
* and 9th digit.
|
|
*
|
|
* @param aMvnoData
|
|
* MVNO pattern.
|
|
* @param aImsi
|
|
* IMSI of this ICC.
|
|
*
|
|
* @return true if matched.
|
|
*/
|
|
_isImsiMatches: function(aMvnoData, aImsi) {
|
|
// This should not be an error, but a mismatch.
|
|
if (aMvnoData.length > aImsi.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let i = 0; i < aMvnoData.length; i++) {
|
|
let c = aMvnoData[i];
|
|
if ((c !== 'x') && (c !== 'X') && (c !== aImsi[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* nsIIcc interface.
|
|
*/
|
|
iccInfo: null,
|
|
cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
|
|
|
|
registerListener: function(aListener) {
|
|
if (this._listeners.indexOf(aListener) >= 0) {
|
|
throw Cr.NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
this._listeners.push(aListener);
|
|
},
|
|
|
|
unregisterListener: function(aListener) {
|
|
let index = this._listeners.indexOf(aListener);
|
|
if (index >= 0) {
|
|
this._listeners.splice(index, 1);
|
|
}
|
|
},
|
|
|
|
getCardLockEnabled: function(aLockType, aCallback) {
|
|
this._radioInterface.sendWorkerMessage("iccGetCardLockEnabled",
|
|
{ lockType: aLockType },
|
|
(aResponse) => {
|
|
if (aResponse.errorMsg) {
|
|
aCallback.notifyError(aResponse.errorMsg);
|
|
return;
|
|
}
|
|
|
|
aCallback.notifySuccessWithBoolean(aResponse.enabled);
|
|
});
|
|
},
|
|
|
|
unlockCardLock: function(aLockType, aPassword, aNewPin, aCallback) {
|
|
this._modifyCardLock("iccUnlockCardLock",
|
|
{ lockType: aLockType,
|
|
password: aPassword,
|
|
newPin: aNewPin },
|
|
aCallback);
|
|
},
|
|
|
|
setCardLockEnabled: function(aLockType, aPassword, aEnabled, aCallback) {
|
|
this._modifyCardLock("iccSetCardLockEnabled",
|
|
{ lockType: aLockType,
|
|
password: aPassword,
|
|
enabled: aEnabled },
|
|
aCallback);
|
|
},
|
|
|
|
changeCardLockPassword: function(aLockType, aPassword, aNewPassword, aCallback) {
|
|
this._modifyCardLock("iccChangeCardLockPassword",
|
|
{ lockType: aLockType,
|
|
password: aPassword,
|
|
newPassword: aNewPassword, },
|
|
aCallback);
|
|
},
|
|
|
|
getCardLockRetryCount: function(aLockType, aCallback) {
|
|
this._radioInterface.sendWorkerMessage("iccGetCardLockRetryCount",
|
|
{ lockType: aLockType },
|
|
(aResponse) => {
|
|
if (aResponse.errorMsg) {
|
|
aCallback.notifyError(aResponse.errorMsg);
|
|
return;
|
|
}
|
|
|
|
aCallback.notifyGetCardLockRetryCount(aResponse.retryCount);
|
|
});
|
|
},
|
|
|
|
matchMvno: function(aMvnoType, aMvnoData, aCallback) {
|
|
if (!aMvnoData) {
|
|
aCallback.notifyError(RIL.GECKO_ERROR_INVALID_PARAMETER);
|
|
return;
|
|
}
|
|
|
|
switch (aMvnoType) {
|
|
case Ci.nsIIcc.CARD_MVNO_TYPE_IMSI:
|
|
let imsi = this._imsi;
|
|
if (!imsi) {
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
break;
|
|
}
|
|
aCallback.notifySuccessWithBoolean(
|
|
this._isImsiMatches(aMvnoData, imsi));
|
|
break;
|
|
case Ci.nsIIcc.CARD_MVNO_TYPE_SPN:
|
|
let spn = this.iccInfo && this.iccInfo.spn;
|
|
if (!spn) {
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
break;
|
|
}
|
|
aCallback.notifySuccessWithBoolean(spn == aMvnoData);
|
|
break;
|
|
case Ci.nsIIcc.CARD_MVNO_TYPE_GID:
|
|
this._radioInterface.sendWorkerMessage("getGID1",
|
|
null,
|
|
(aResponse) => {
|
|
let gid = aResponse.gid1;
|
|
let mvnoDataLength = aMvnoData.length;
|
|
|
|
if (!gid) {
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
} else if (mvnoDataLength > gid.length) {
|
|
aCallback.notifySuccessWithBoolean(false);
|
|
} else {
|
|
let result =
|
|
gid.substring(0, mvnoDataLength).toLowerCase() ==
|
|
aMvnoData.toLowerCase();
|
|
aCallback.notifySuccessWithBoolean(result);
|
|
}
|
|
});
|
|
break;
|
|
default:
|
|
aCallback.notifyError(RIL.GECKO_ERROR_MODE_NOT_SUPPORTED);
|
|
break;
|
|
}
|
|
},
|
|
|
|
getServiceStateEnabled: function(aService, aCallback) {
|
|
this._radioInterface.sendWorkerMessage("getIccServiceState",
|
|
{ service: aService },
|
|
(aResponse) => {
|
|
if (aResponse.errorMsg) {
|
|
aCallback.notifyError(aResponse.errorMsg);
|
|
return;
|
|
}
|
|
|
|
aCallback.notifySuccessWithBoolean(aResponse.result);
|
|
});
|
|
}
|
|
};
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IccService]);
|