Bug 1051746 - [MobileID] Get the proper ICC info by service ID. r=spenrose

This commit is contained in:
Fernando Jiménez 2014-09-23 19:58:15 +02:00
parent 49fb783eeb
commit 9733d171e7
5 changed files with 254 additions and 43 deletions

View File

@ -52,15 +52,15 @@ XPCOMUtils.defineLazyServiceGetter(this, "appsService",
"nsIAppsService");
#ifdef MOZ_B2G_RIL
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
XPCOMUtils.defineLazyServiceGetter(this, "Ril",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
XPCOMUtils.defineLazyServiceGetter(this, "IccProvider",
"@mozilla.org/ril/content-helper;1",
"nsIIccProvider");
XPCOMUtils.defineLazyServiceGetter(this, "mobileConnectionService",
XPCOMUtils.defineLazyServiceGetter(this, "MobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
#endif
@ -104,30 +104,84 @@ this.MobileIdentityManager = {
this.messageManagers = null;
},
/*********************************************************
* Getters
********************************************************/
#ifdef MOZ_B2G_RIL
// We have these getters to allow mocking RIL stuff from the tests.
get ril() {
if (this._ril) {
return this._ril;
}
return Ril;
},
get iccProvider() {
if (this._iccProvider) {
return this._iccProvider;
}
return IccProvider;
},
get mobileConnectionService() {
if (this._mobileConnectionService) {
return this._mobileConnectionService;
}
return MobileConnectionService;
},
#endif
get iccInfo() {
if (this._iccInfo) {
return this._iccInfo;
}
#ifdef MOZ_B2G_RIL
let self = this;
let iccListener = {
notifyStkCommand: function() {},
notifyStkSessionEnd: function() {},
notifyCardStateChanged: function() {},
notifyIccInfoChanged: function() {
// If we receive a notification about an ICC info change, we clear
// the ICC related caches so they can be rebuilt with the new changes.
log.debug("ICC info changed observed. Clearing caches");
// We don't need to keep listening for changes until we rebuild the
// cache again.
for (let i = 0; i < self._iccInfo.length; i++) {
self.iccProvider.unregisterIccMsg(self._iccInfo[i].clientId,
iccListener);
}
self._iccInfo = null;
self._iccIds = null;
}
};
// _iccInfo is a local cache containing the information about the SIM cards
// that it is interesting for the Mobile ID flow.
// The index of this array does not necesarily need to match the real
// identifier of the SIM card ("clientId" or "serviceId" in RIL language).
this._iccInfo = [];
for (let i = 0; i < gRil.numRadioInterfaces; i++) {
let rilContext = gRil.getRadioInterface(i).rilContext;
for (let i = 0; i < this.ril.numRadioInterfaces; i++) {
let rilContext = this.ril.getRadioInterface(i).rilContext;
if (!rilContext) {
log.warn("Tried to get the RIL context for an invalid service ID " + i);
continue;
}
let info = rilContext.iccInfo;
if (!info) {
log.warn("No ICC info");
continue;
}
let connection = mobileConnectionService.getItemByServiceId(i);
let connection = this.mobileConnectionService.getItemByServiceId(i);
let voice = connection && connection.voice;
let data = connection && connection.data;
let operator = null;
@ -144,15 +198,21 @@ this.MobileIdentityManager = {
}
this._iccInfo.push({
// Because it is possible that the _iccInfo array index doesn't match
// the real client ID, we need to store this value for later usage.
clientId: i,
iccId: info.iccid,
mcc: info.mcc,
mnc: info.mnc,
// GSM SIMs may have MSISDN while CDMA SIMs may have MDN
msisdn: info.msisdn || info.mdn || null,
operator: operator,
serviceId: i,
roaming: voice && voice.roaming
});
// We need to subscribe to ICC change notifications so we can refresh
// the cache if any change is observed.
this.iccProvider.registerIccMsg(i, iccListener);
}
return this._iccInfo;
@ -213,9 +273,9 @@ this.MobileIdentityManager = {
log.debug("getVerificationOptionsForIcc " + aServiceId);
log.debug("iccInfo ${}", this.iccInfo[aServiceId]);
// First of all we need to check if we already have existing credentials
// for the given SIM information (ICC id or MSISDN). If we have no valid
// credentials, we have to check with the server which options to do we
// have to verify the associated phone number.
// for the given SIM information (ICC ID or MSISDN). If we have no valid
// credentials, we have to check with the server which options do we have
// to verify the associated phone number.
return this.credStore.getByIccId(this.iccInfo[aServiceId].iccId)
.then(
(creds) => {
@ -566,8 +626,11 @@ this.MobileIdentityManager = {
// This prompt will be considered as the permission prompt and its choice
// will be remembered per origin by default.
prompt: function prompt(aPrincipal, aManifestURL, aPhoneInfo) {
log.debug("prompt " + aPrincipal + ", " + aManifestURL + ", " +
aPhoneInfo);
log.debug("prompt ${principal} ${manifest} ${phoneInfo}", {
principal: aPrincipal,
manifest: aManifestURL,
phoneInfo: aPhoneInfo
});
let phoneInfoArray = [];
@ -578,19 +641,22 @@ this.MobileIdentityManager = {
if (this.iccInfo) {
for (let i = 0; i < this.iccInfo.length; i++) {
// If we don't know the msisdn, there is no previous credentials and
// a silent verification is not possible, we don't allow the user to
// choose this option.
if (!this.iccInfo[i].msisdn && !this.iccInfo[i].credentials &&
!this.iccInfo[i].canDoSilentVerification) {
// a silent verification is not possible or if the msisdn is the one
// that is already chosen, we don't list this SIM as an option.
if ((!this.iccInfo[i].msisdn && !this.iccInfo[i].credentials &&
!this.iccInfo[i].canDoSilentVerification) ||
((aPhoneInfo) &&
(this.iccInfo[i].msisdn == aPhoneInfo.msisdn ||
this.iccInfo[i].iccId == aPhoneInfo.iccId))) {
continue;
}
let phoneInfo = new MobileIdentityUIGluePhoneInfo(
this.iccInfo[i].msisdn,
this.iccInfo[i].operator,
i, // service ID
false, // external
false // primary
i, // service ID
this.iccInfo[i].iccId, // iccId
false // primary
);
phoneInfoArray.push(phoneInfo);
}
@ -674,7 +740,7 @@ this.MobileIdentityManager = {
aCreds.msisdn,
null, // operator
undefined, // service ID
!!aCreds.iccId, // external
aCreds.iccId, // iccId
true // primary
);
}

View File

@ -8,13 +8,14 @@ this.EXPORTED_SYMBOLS = ["MobileIdentityUIGluePhoneInfo",
"MobileIdentityUIGluePromptResult"];
this.MobileIdentityUIGluePhoneInfo = function (aMsisdn, aOperator, aServiceId,
aExternal, aPrimary) {
aIccId, aPrimary) {
this.msisdn = aMsisdn;
this.operator = aOperator;
this.serviceId = aServiceId;
this.iccId = aIccId;
// A phone number is considered "external" when it doesn't or we don't know
// if it does belong to any of the device SIM cards.
this.external = aExternal;
this.external = !!aIccId;
this.primary = aPrimary;
}

View File

@ -11,6 +11,9 @@ Cu.import("resource://gre/modules/Services.jsm");
(function initMobileIdTestingInfrastructure() {
do_get_profile();
const PREF_FORCE_HTTPS = "services.mobileid.forcehttps";
Services.prefs.setBoolPref(PREF_FORCE_HTTPS, false);
Services.prefs.setCharPref("services.mobileid.loglevel", "Debug");
Services.prefs.setCharPref("services.mobileid.server.uri",
"https://dummyurl.com");
}).call(this);

View File

@ -9,11 +9,6 @@ Cu.import("resource://gre/modules/MobileIdentityClient.jsm");
/* Setup */
do_get_profile();
const PREF_FORCE_HTTPS = "services.mobileid.forcehttps";
Services.prefs.setBoolPref(PREF_FORCE_HTTPS, false);
let client;
let server = new HttpServer();

View File

@ -7,16 +7,6 @@ const Cm = Components.manager;
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// We need to set the server url pref before importing MobileIdentityManager.
// Otherwise, it won't be able to import it as getting the non existing pref
// will throw.
Services.prefs.setCharPref("services.mobileid.server.uri",
"https://dummyurl.com");
// Set do_printging on.
Services.prefs.setCharPref("services.mobileid.loglevel", "do_print");
Cu.import("resource://gre/modules/MobileIdentityManager.jsm");
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
@ -39,9 +29,58 @@ const ANOTHER_PHONE_NUMBER = "+44123123123";
const VERIFICATION_CODE = "123456";
const SESSION_TOKEN = "aSessionToken";
const ICC_ID = "aIccId";
const ANOTHER_ICC_ID = "anotherIccId";
const MNC = "aMnc";
const MCC = 214;
const ANOTHER_MNC = "anotherMnc";
const MCC = "aMcc";
const ANOTHER_MCC = "anotherMcc";
const OPERATOR = "aOperator";
const ANOTHER_OPERATOR = "anotherOperator";
const RADIO_INTERFACE = {
rilContext: {
iccInfo: {
iccid: ICC_ID,
mcc: MCC,
mnc: MNC,
msisdn: PHONE_NUMBER,
operator: OPERATOR
}
},
voice: {
network: {
shortName: OPERATOR
},
roaming: false
},
data: {
network: {
shortName: OPERATOR
}
}
};
const ANOTHER_RADIO_INTERFACE = {
rilContext: {
iccInfo: {
iccid: ANOTHER_ICC_ID,
mcc: ANOTHER_MCC,
mnc: ANOTHER_MNC,
msisdn: ANOTHER_PHONE_NUMBER,
operator: ANOTHER_OPERATOR
}
},
voice: {
network: {
shortName: ANOTHER_OPERATOR
},
roaming: false
},
data: {
network: {
shortName: ANOTHER_OPERATOR
}
}
};
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
// === Helpers ===
@ -363,7 +402,7 @@ function cleanup() {
MobileIdentityManager.credStore = kMobileIdentityCredStore;
MobileIdentityManager.client = kMobileIdentityClient;
MobileIdentityManager.ui = null;
MobileIdentityManager.iccInfo = null;
MobileIdentityManager._iccInfo = [];
removePermission(ORIGIN);
}
@ -394,6 +433,7 @@ add_test(function() {
MobileIdentityManager.credStore = credStore;
let client = new MockClient();
MobileIdentityManager.client = client;
MobileIdentityManager._iccInfo = [];
let promiseId = Date.now();
let mm = {
@ -898,7 +938,7 @@ add_test(function() {
let client = new MockClient();
MobileIdentityManager.client = client;
MobileIdentityManager.iccInfo = [];
MobileIdentityManager._iccInfo = [];
let promiseId = Date.now();
let mm = {
@ -990,7 +1030,7 @@ add_test(function() {
});
MobileIdentityManager.client = client;
MobileIdentityManager.iccInfo = [];
MobileIdentityManager._iccInfo = [];
let promiseId = Date.now();
let mm = {
@ -1317,3 +1357,109 @@ add_test(function() {
}
});
});
add_test(function() {
do_print("= ICC info change =");
do_register_cleanup(cleanup);
do_test_pending();
let _sessionToken = Date.now();
MobileIdentityManager._iccInfo = null;
MobileIdentityManager._iccIds = null;
MobileIdentityManager._ril = {
_interfaces: [RADIO_INTERFACE, ANOTHER_RADIO_INTERFACE],
get numRadioInterfaces() {
return this._interfaces.length;
},
getRadioInterface: function(aIndex) {
return this._interfaces[aIndex];
}
};
MobileIdentityManager._mobileConnectionService = {
_interfaces: [RADIO_INTERFACE, ANOTHER_RADIO_INTERFACE],
getVoiceConnectionInfo: function(aIndex) {
return this._interfaces[aIndex].voice;
},
getDataConnectionInfo: function(aIndex) {
return this._interfaces[aIndex].data;
}
};
MobileIdentityManager._iccProvider = {
_listeners: [],
registerIccMsg: function(aClientId, aIccListener) {
this._listeners.push(aIccListener);
},
unregisterIccMsg: function() {
this._listeners.pop();
}
};
let ui = new MockUi();
ui.startFlow = function() {
// At this point we've already built the ICC cache.
let interfaces = MobileIdentityManager._ril._interfaces;
for (let i = 0; i < interfaces.length; i++) {
let interfaceIccInfo = interfaces[i].rilContext.iccInfo;
let mIdIccInfo = MobileIdentityManager._iccInfo[i];
do_check_eq(interfaceIccInfo.iccid, mIdIccInfo.iccId);
do_check_eq(interfaceIccInfo.mcc, mIdIccInfo.mcc);
do_check_eq(interfaceIccInfo.mnc, mIdIccInfo.mnc);
do_check_eq(interfaceIccInfo.msisdn, mIdIccInfo.msisdn);
do_check_eq(interfaceIccInfo.operator, mIdIccInfo.operator);
}
// We should have listeners for each valid icc.
do_check_eq(MobileIdentityManager._iccProvider._listeners.length, 2);
// We can mock an ICC change event at this point.
MobileIdentityManager._iccProvider._listeners[0].notifyIccInfoChanged();
// After the ICC change event the caches should be null.
do_check_null(MobileIdentityManager._iccInfo);
do_check_null(MobileIdentityManager._iccIds);
// And we should have unregistered all listeners for ICC change events.
do_check_eq(MobileIdentityManager._iccProvider._listeners.length, 0);
do_test_finished();
run_next_test();
};
MobileIdentityManager.ui = ui;
let credStore = new MockCredStore();
credStore.getByOrigin = function() {
// Initially the ICC caches should be null.
do_check_null(MobileIdentityManager._iccInfo);
do_check_null(MobileIdentityManager._iccIds);
return Promise.resolve(null);
};
MobileIdentityManager.credStore = credStore;
let client = new MockClient();
MobileIdentityManager.client = client;
let promiseId = Date.now();
let mm = {
sendAsyncMessage: function() {}
};
addPermission(Ci.nsIPermissionManager.ALLOW_ACTION);
MobileIdentityManager.receiveMessage({
name: GET_ASSERTION_IPC_MSG,
principal: PRINCIPAL,
target: mm,
json: {
promiseId: promiseId,
options: {}
}
});
});