VulpesOS/gaia/shared/js/operator_variant_helper.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

401 lines
13 KiB
JavaScript
Executable file

/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
/**
* An OperatorVariantHelper object can be used to trigger customizations
* in an application at startup.
*
* The object will call the listener passed to it during construction
* if the MCC/MNC pair present on the SIM card differs from the previously
* seen pair. The listener will also be called during first boot up of the
* device.
*
* Optionally, one can also use an OperatorVariantListener object to listen
* to SIM card changes during runtime, using the 'listen' method. This can
* enable applications to support multi-SIM devices if appropriate for the
* functionality it provides.
*
* @param {String} iccId - ICC id from the card.
* @param {Numeric} iccCardIndex - Index of ICC card.
* @param {Function} listener - A listener function that will be called when
* a check occurs and the SIM information has
* changed. The function signature should match
* the following: function(mcc, mnc).
* @param {String} persistKey - The settings key which will be used to persist
* if the customizations have been applied. We
* recommend using the application package name
* as the root for your key along with
* 'customization'. The persisted setting is a
* boolean value, where true means the
* customizations have been applied.
* @param {Boolean} checkNow - Perform a check for SIM card change immediately.
* Defaults to true.
*
* @constructor
*/
function OperatorVariantHelper(iccId,
iccCardIndex,
listener,
persistKey,
checkNow) {
var errMsg = null;
// mozIccManager must be present.
var iccManager = window.navigator.mozIccManager;
if (!iccManager) {
errMsg = 'Expected mozIccManager to be present.';
console.error(errMsg);
throw new Error(errMsg);
}
// And mozSettings must be present.
var settings = window.navigator.mozSettings;
if (!settings) {
errMsg = 'Expected mozSettings to be present.';
console.error(errMsg);
throw new Error(errMsg);
}
if ((iccId === '0') || (iccCardIndex < 0)) {
throw new Error('iccId and iccCardIndex arguments must have a value!');
}
this._iccId = iccId;
this._iccCardIndex = iccCardIndex;
if (listener === undefined || typeof listener !== 'function') {
throw new Error('listener argument must be a function!');
}
this._listener = listener;
if (persistKey === undefined || typeof persistKey !== 'string') {
throw new Error('persistKey argument must be a string!');
}
this._persistKey = persistKey;
if (!!checkNow) {
this.customize();
}
}
OperatorVariantHelper.prototype = {
// The listener to call when the SIM card is new or has changed.
_listener: null,
// The actual registered listener (since we use bind we need this).
_addedListener: null,
//
_iccCard: null,
// Cached ICC information.
_iccSettings: { mcc: '', mnc: '', iccId: '' },
// Settings persistence key.
_persistKey: null,
// Are operator variant customizations disabled?
_disableAll: false,
// The mozSettings key for the saved ICCIDs.
get ICCID_SETTINGS_KEY() {
return 'operatorvariant.iccId';
},
// The mozSettings key for the saved MCCs.
get MCC_SETTINGS_KEY() {
return 'operatorvariant.mcc';
},
// The mozSettings key for the saved MNCs.
get MNC_SETTINGS_KEY() {
return 'operatorvariant.mnc';
},
// The prefs key for disabling all customizations.
get OPERATOR_VARIANT_DISABLE_ALL_KEY() {
return 'operatorvariant.disableAll';
},
// Getter for global objects we use frequently.
get settings() {
return window.navigator.mozSettings;
},
/**
* Get the saved ICC Settings (MCC/MNC).
*/
getICCSettings: function() {
var transaction = this.settings.createLock();
var mccRequest = transaction.get(this.MCC_SETTINGS_KEY);
mccRequest.onsuccess = (function() {
var mccs = mccRequest.result[this.MCC_SETTINGS_KEY];
if (!mccs) {
this._iccSettings.mcc = '000';
} else if (!Array.isArray(mccs)) {
this._iccSettings.mcc = mccs;
} else if (!mccs[this._iccCardIndex]) {
this._iccSettings.mcc = '000';
} else {
this._iccSettings.mcc = mccs[this._iccCardIndex];
}
var mncRequest = transaction.get(this.MNC_SETTINGS_KEY);
mncRequest.onsuccess = (function() {
var mncs = mncRequest.result[this.MNC_SETTINGS_KEY];
if (!mncs) {
this._iccSettings.mnc = '00';
} else if (!Array.isArray(mncs)) {
this._iccSettings.mnc = mncs;
} else if (!mncs[this._iccCardIndex]) {
this._iccSettings.mnc = '00';
} else {
this._iccSettings.mnc = mncs[this._iccCardIndex];
}
var iccIdsRequest = transaction.get(this.ICCID_SETTINGS_KEY);
iccIdsRequest.onsuccess = (function() {
var iccIds = iccIdsRequest.result[this.ICCID_SETTINGS_KEY];
this._iccSettings.iccId = iccIds && iccIds[this._iccCardIndex];
this.checkICCInfo();
}).bind(this);
}).bind(this);
}).bind(this);
},
/**
* Verify the saved ICC Settings vs what the SIM card is reporting. If they
* differ we will call the listener that was registered during construction
* to enable customization.
*/
checkICCInfo: function() {
var iccManager = window.navigator.mozIccManager;
this._iccCard = iccManager.getIccById(this._iccId);
if (!this._iccCard) {
return;
}
if (!this._iccCard.iccInfo || this._iccCard.cardState !== 'ready') {
return;
}
// XXX sometimes we get 000/00 for mcc/mnc, even when cardState === 'ready'
var mcc = this._iccCard.iccInfo.mcc || '000';
var mnc = this._iccCard.iccInfo.mnc || '00';
var iccId = this._iccId;
var transaction;
if (mcc === '000') {
return;
}
// ensure that the iccSettings have been retrieved
if (this._iccSettings.iccId === '') {
return;
}
if (iccId !== this._iccSettings.iccId) {
if (this._addedListener) {
try {
// apply new settings
this._listener(mcc, mnc);
}
catch (e) {
console.error('Listener threw an error!', e);
}
this.listen(false);
}
} else {
// Check whether we ran customizations already.
transaction = this.settings.createLock();
var persistKeyGetRequest = transaction.get(this._persistKey);
persistKeyGetRequest.onsuccess = (function persistKeyGetRequestCb() {
// Looks like we didn't run customizations, apply settings.
if (!persistKeyGetRequest.result[this._persistKey]) {
if (this._addedListener) {
try {
// apply new APN settings
this._listener(mcc, mnc, true);
}
catch (e) {
console.error('Listener threw an error!', e);
}
}
}
this.listen(false);
}).bind(this);
}
// store current mcc/mnc info in the settings
transaction = this.settings.createLock();
var mccRequest = transaction.get(this.MCC_SETTINGS_KEY);
mccRequest.onsuccess = (function() {
var mccs = mccRequest.result[this.MCC_SETTINGS_KEY];
if (!mccs || !Array.isArray(mccs)) {
mccs = ['000', '000'];
}
mccs[this._iccCardIndex] = mcc;
var mccSettings = {};
mccSettings[this.MCC_SETTINGS_KEY] = mccs;
transaction.set(mccSettings);
var mncRequest = transaction.get(this.MNC_SETTINGS_KEY);
mncRequest.onsuccess = (function() {
var mncs = mncRequest.result[this.MNC_SETTINGS_KEY];
if (!mncs || !Array.isArray(mncs)) {
mncs = ['00', '00'];
}
mncs[this._iccCardIndex] = mnc;
var mncSettings = {};
mncSettings[this.MNC_SETTINGS_KEY] = mncs;
transaction.set(mncSettings);
var iccIdsRequest = transaction.get(this.ICCID_SETTINGS_KEY);
iccIdsRequest.onsuccess = (function() {
var iccIds = iccIdsRequest.result[this.ICCID_SETTINGS_KEY];
if (!iccIds || !Array.isArray(iccIds)) {
iccIds = [null, null];
}
iccIds[this._iccCardIndex] = iccId;
var iccIdSettings = {};
iccIdSettings[this.ICCID_SETTINGS_KEY] = iccIds;
transaction.set(iccIdSettings);
this._iccSettings.mcc = mcc;
this._iccSettings.mnc = mnc;
this._iccSettings.iccId = iccId;
}).bind(this);
}).bind(this);
}).bind(this);
},
/**
* Persist that the customizations have been applied. Uses the 'persistKey'
* passed in during construction of the object.
*/
applied: function() {
var transaction = this.settings.createLock();
// Persist the fact that we ran customizations.
var item = {};
item[this._persistKey] = true;
var opVariantReq = transaction.set(item);
// We're running in test mode, just return.
if (opVariantReq === undefined) {
return;
}
// If we error we could end up running the customizations again! Ensure
// we surface this to developers.
opVariantReq.onerror = (function(event) {
console.error('Failed to set',
this._persistKey,
'to true.',
'Customizations may run again!');
}).bind(this);
},
/**
* Revert persisted key that tracks if customizations have been applied.
*/
revert: function() {
var transaction = this.settings.createLock();
// Persist the fact that we ran customizations.
var item = {};
item[this._persistKey] = false;
var opVariantReq = transaction.set(item);
// We're running in test mode, just return.
if (opVariantReq === undefined) {
return;
}
opVariantReq.onerror = (function(event) {
console.error('Failed to set',
this._persistKey,
'to false.',
'Customizations will not be able to be applied again.');
}).bind(this);
},
/**
* Start the customization process.
*/
customize: function() {
this.getICCSettings();
},
/**
* Listen for further changes in MCC/MNC values, will trigger customization
* even if customizations have already run. Be careful when using this, it's
* meant for settings that *should* change at runtime.
*
* @param {Boolean} listenForChange - Listen for changes in MCC/MNC values
* during runtime. For multi-sim support.
* Defaults to true. Call this method
* with 'false' to stop listening.
*/
listen: function(listenForChange) {
// Defaults to true so we need to make sure it's set.
var iccManager;
if (listenForChange === undefined) {
listenForChange = true;
}
// Register listener if we're listening for changes.
if (listenForChange) {
// We need to keep a reference to the added listener for removal later.
this._addedListener = this.customize.bind(this);
iccManager = window.navigator.mozIccManager;
this._iccCard = iccManager.getIccById(this._iccId);
if (this._iccCard) {
// Add the actual bound listener.
this._iccCard.addEventListener(
'iccinfochange',
this._addedListener
);
this._iccCard.addEventListener(
'cardstatechange',
this._addedListener
);
} else {
iccManager.oniccdetected = (function iccDetectedHandler(evt) {
if (this._iccId !== evt.iccId) {
return;
}
this._iccCard = iccManager.getIccById(this._iccId);
// Add the actual bound listener.
this._iccCard.addEventListener(
'iccinfochange',
this._addedListener
);
this._iccCard.addEventListener(
'cardstatechange',
this._addedListener
);
}).bind(this);
}
return;
}
if (this._addedListener) {
iccManager = window.navigator.mozIccManager;
this._iccCard = iccManager.getIccById(this._iccId);
if (this._iccCard) {
// Otherwise, unregister.
this._iccCard.removeEventListener(
'iccinfochange',
this._addedListener
);
this._iccCard.removeEventListener(
'cardstatechange',
this._addedListener
);
}
// Clear our reference to the bound listener.
this._addedListener = null;
}
}
};