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.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,428 @@
'use strict';
document.addEventListener('DOMContentLoaded', function onload() {
var DEBUG = false;
var OPERATOR_VARIANT_FILE = '../apn.json';
var GNOME_DB_FILE = 'service_providers.xml';
var ANDROID_DB_FILE = 'apns_conf.xml';
var LOCAL_ANDROID_DB_FILE = 'apns_conf_local.xml';
var OPERATOR_VARIANT_DB_FILE = 'operator_variant.xml';
var gGnomeDB = null;
var gAndroidDB = null;
var gOperatorVariantDB = null;
/**
* XML helpers: load & query XML databases
*/
function loadXML(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
return xhr.responseXML;
}
function queryXML(xmlDocument, query) {
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(xmlDocument);
return xpe.evaluate(query, xmlDocument, nsResolver, 0, null);
}
/**
* Merge the Android and Gnome APN and the Operator Variant databases
*/
function queryAndroidDB(mcc, mnc) {
var query = '//apn';
if (mcc) {
query += '[@mcc=' + mcc + ']'; // Mobile Country Code
}
if (mnc) {
query += '[@mnc=' + mnc + ']'; // Mobile Network Code
}
var result = queryXML(gAndroidDB, query);
var res = result.iterateNext();
var found = [];
while (res) { // turn each resulting XML element into a JS object
var apn = {};
for (var i = 0; i < res.attributes.length; i++) {
var name = res.attributes[i].name;
var value = res.attributes[i].value;
if (name == 'type') { // array of comma-separated values
apn.type = value.split(',');
} else { // all other attributes are plain strings
apn[name] = value;
}
}
found.push(apn);
res = result.iterateNext();
}
return found;
}
function queryGnomeDB(mcc, mnc, setting) {
var query = '//gsm[network-id' + '[@mcc=' + mcc + '][@mnc=' + mnc + ']' +
']/' + setting;
var result = queryXML(gGnomeDB, query);
var node = result.iterateNext();
return node ? node.textContent : '';
}
function queryOperatorVariantDB(mcc, mnc) {
var query = '//operator' + '[@mcc=' + mcc + '][@mnc=' + mnc + ']';
var result = queryXML(gOperatorVariantDB, query);
var res = result.iterateNext();
var found = [];
while (res) { // turn each resulting XML element into a JS object
var operatorSettings = {};
for (var i = 0; i < res.attributes.length; i++) {
var name = res.attributes[i].name;
var value = res.attributes[i].value;
operatorSettings[name] = value;
}
found.push(operatorSettings);
res = result.iterateNext();
}
return found.length ? found[0] : null;
}
function padLeft(num, length) {
var r = String(num);
while (r.length < length) {
r = '0' + r;
}
return r;
}
function mergeDBs() {
var apn = {};
for (var mcc = 1; mcc < 999; mcc++) {
var country = {};
var _mcc = padLeft(mcc, 3);
var result = queryAndroidDB(_mcc);
if (result && result.length) {
result.sort();
for (var i = 0; i < result.length; i++) {
/* jshint loopfunc: true */
var mnc = result[i].mnc;
var operatorVariantSettings = {};
var voicemail = queryGnomeDB(_mcc, mnc, 'voicemail');
if (voicemail) {
operatorVariantSettings.voicemail = voicemail;
if (DEBUG) {
console.log(operatorVariantSettings.voicemail + ': ' + voicemail);
}
}
var otherSettings = queryOperatorVariantDB(_mcc, mnc);
if (otherSettings) {
if (DEBUG) {
console.log('Other operator settings: ' +
JSON.stringify(otherSettings));
}
if (!operatorVariantSettings.voicemail) {
voicemail = otherSettings.voicemail;
if (voicemail) {
operatorVariantSettings.voicemail = voicemail;
}
}
var enableStrict7BitEncodingForSms =
otherSettings.enableStrict7BitEncodingForSms;
if (enableStrict7BitEncodingForSms) {
operatorVariantSettings.enableStrict7BitEncodingForSms =
enableStrict7BitEncodingForSms == 'true';
}
var cellBroadcastSearchList =
otherSettings.cellBroadcastSearchList;
if (cellBroadcastSearchList) {
var searchListObj = {};
var lists = cellBroadcastSearchList.split('|');
lists.forEach(function(list) {
var parts = list.split(':');
searchListObj[parts[0]] = parts[1];
});
operatorVariantSettings.cellBroadcastSearchList = searchListObj;
}
var operatorSizeLimitation =
otherSettings.operatorSizeLimitation;
if (operatorSizeLimitation) {
operatorVariantSettings.operatorSizeLimitation =
+operatorSizeLimitation;
}
var skipProxy = otherSettings.skipProxy;
if (skipProxy == 'true') {
var skipProxyFor = otherSettings.skipProxyFor;
if (skipProxyFor) {
if (skipProxyFor.indexOf(result[i].carrier) != -1) {
if (DEBUG) {
console.log('Skip proxy setting for: ' + result[i].carrier);
}
result[i].proxy = result[i].port = '';
}
}
}
}
delete(result[i].mcc);
delete(result[i].mnc);
if (country[mnc]) {
if (DEBUG) { // warn about the duplicate (mcc, mnc) tuple
if (country[mnc].length == 1) {
console.log('duplicate mcc/mnc: ' + _mcc + '/' + mnc);
console.log(' - ' + country[mnc][0].carrier);
}
console.log(' - ' + result[i].carrier);
}
country[mnc].push(result[i]);
} else {
country[mnc] = [result[i]];
if (voicemail || otherSettings) {
operatorVariantSettings.type = [];
operatorVariantSettings.type.push('operatorvariant');
country[mnc].push(operatorVariantSettings);
}
}
}
apn[_mcc] = country;
}
}
return apn;
}
/**
* User Interface
*/
var gAPN;
var apnPrefNames = {
'default': {
'ril.data.carrier': 'carrier',
'ril.data.apn': 'apn',
'ril.data.user': 'user',
'ril.data.passwd': 'password',
'ril.data.httpProxyHost': 'proxy',
'ril.data.httpProxyPort': 'port',
'ril.data.authtype': 'authtype',
'ril.data.mtu': 'mtu'
},
'supl': {
'ril.supl.carrier': 'carrier',
'ril.supl.apn': 'apn',
'ril.supl.user': 'user',
'ril.supl.passwd': 'password',
'ril.supl.httpProxyHost': 'proxy',
'ril.supl.httpProxyPort': 'port',
'ril.supl.authtype': 'authtype',
'ril.supl.mtu': 'mtu'
},
'mms': {
'ril.mms.carrier': 'carrier',
'ril.mms.apn': 'apn',
'ril.mms.user': 'user',
'ril.mms.passwd': 'password',
'ril.mms.httpProxyHost': 'proxy',
'ril.mms.httpProxyPort': 'port',
'ril.mms.mmsc': 'mmsc',
'ril.mms.mmsproxy': 'mmsproxy',
'ril.mms.mmsport': 'mmsport',
'ril.mms.authtype': 'authtype',
'ril.mms.mtu': 'mtu'
},
'dun': {
'ril.dun.carrier': 'carrier',
'ril.dun.apn': 'apn',
'ril.dun.user': 'user',
'ril.dun.passwd': 'password',
'ril.dun.httpProxyHost': 'proxy',
'ril.dun.httpProxyPort': 'port',
'ril.dun.authtype': 'authtype',
'ril.dun.mtu': 'mtu'
},
'ims': {
'ril.ims.carrier': 'carrier',
'ril.ims.apn': 'apn',
'ril.ims.user': 'user',
'ril.ims.passwd': 'password',
'ril.ims.httpProxyHost': 'proxy',
'ril.ims.httpProxyPort': 'port',
'ril.ims.authtype': 'authtype',
'ril.ims.mtu': 'mtu'
},
'fota': {
'ril.fota.carrier': 'carrier',
'ril.fota.apn': 'apn',
'ril.fota.user': 'user',
'ril.fota.passwd': 'password',
'ril.fota.httpProxyHost': 'proxy',
'ril.fota.httpProxyPort': 'port',
'ril.fota.authtype': 'authtype',
'ril.fota.mtu': 'mtu'
},
'operatorvariant': {
'ril.iccInfo.mbdn': 'voicemail',
'ril.sms.strict7BitEncoding.enabled': 'enableStrict7BitEncodingForSms',
'ril.cellbroadcast.searchlist': 'cellBroadcastSearchList',
'dom.mms.operatorSizeLimitation': 'operatorSizeLimitation'
}
};
var booleanPrefNames = [
'ril.sms.strict7BitEncoding.enabled'
];
function loadDB(output, callback) {
output.textContent = '\n loading database...';
var xhr = new XMLHttpRequest();
xhr.open('GET', OPERATOR_VARIANT_FILE, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status === 0) {
// the JSON database is already available -- we don't use
// xhr.responseType = 'json' to be compatible with Chrome... sigh.
gAPN = JSON.parse(xhr.responseText);
} else {
// the JSON database is not available, merge the three XML databases
output.textContent = '\n merging databases, this takes a while...';
gAndroidDB = loadXML(ANDROID_DB_FILE);
// First merge the local DB
var localAndroidDB = loadXML(LOCAL_ANDROID_DB_FILE);
var localApns =
localAndroidDB.documentElement.querySelectorAll('apn');
var androidApns;
var androidApn;
for (var localApn of localApns) {
if (localApn.getAttribute('overwrite') &&
localApn.getAttribute('carrier_name')) {
var pattern = '[carrier="' +
localApn.getAttribute('carrier_name') + '"]';
androidApns =
gAndroidDB.documentElement.querySelectorAll(pattern);
var matchFound = false;
for (androidApn of androidApns) {
if (androidApn &&
androidApn.getAttribute('carrier') ===
localApn.getAttribute('carrier_name')) {
if (DEBUG) {
console.log('- replace "' +
androidApn.getAttribute('apn') +
'" to "' + localApn.getAttribute('apn') +
'"');
}
localApn.removeAttribute('overwrite');
localApn.removeAttribute('carrier_name');
var parent = androidApn.parentNode;
parent.insertBefore(localApn, androidApn);
parent.removeChild(androidApn);
matchFound = true;
}
}
if (!matchFound) {
alert('Warning!\nCould not find APN \'' +
localApn.getAttribute('carrier_name') +
'\'. Which is expected to be overwrited.');
}
continue;
}
// use local apn to patch origin carrier name in the Android DB
// if the name is not the correct one (see bug 863126).
// Note: This patch will not function once we get
// the correct names updated in the upstream database.
if (localApn.getAttribute('name')) {
var localPattern = 'apn' +
'[mcc="' + localApn.getAttribute('mcc') + '"]' +
'[mnc="' + localApn.getAttribute('mnc') + '"]';
androidApns =
gAndroidDB.documentElement.querySelectorAll(localPattern);
for (androidApn of androidApns) {
if (androidApn &&
androidApn.getAttribute('carrier') ===
localApn.getAttribute('carrier')) {
if (DEBUG) {
console.log('- replace "' +
androidApn.getAttribute('carrier') +
'" to "' + localApn.getAttribute('name') +
'"');
}
androidApn.setAttribute('carrier',
localApn.getAttribute('name'));
}
}
} else {
gAndroidDB.documentElement.appendChild(localApn);
}
}
// Then the Gnome DB
gGnomeDB = loadXML(GNOME_DB_FILE);
gOperatorVariantDB = loadXML(OPERATOR_VARIANT_DB_FILE);
gAPN = mergeDBs();
}
output.textContent = DEBUG ?
JSON.stringify(gAPN, true, 2) :
JSON.stringify(gAPN, true, 0)
.replace(/("[0-9]+"):{/g, '\n$1: {')
.replace(/("[0-9]+"):\[/g, '\n $1: [')
.replace(/},\n/g, '\n},\n')
.replace(/\[{/g, '[\n {')
.replace(/},{/g, '},\n {')
.replace(/}(],?)/g, '}\n $1')
.replace(/]}}$/, ']\n}\n}');
callback();
}
};
xhr.send();
}
function update() {
var selection = document.getElementById('selection');
var mcc = document.querySelector('input[name=mcc]').value;
var mnc = document.querySelector('input[name=mnc]').value;
var res = gAPN[mcc] ? (gAPN[mcc][mnc] || []) : [];
selection.textContent = JSON.stringify(res, true, 2);
var preferences = document.getElementById('preferences');
var prefs = {};
for (var type in apnPrefNames) {
var apn = {};
for (var i = 0; i < res.length; i++) {
if (res[i].type.indexOf(type) != -1) {
apn = res[i];
break;
}
}
var prefNames = apnPrefNames[type];
for (var key in prefNames) {
var name = apnPrefNames[type][key];
if (booleanPrefNames.indexOf(key) != -1) {
prefs[key] = apn[name] || false;
} else {
prefs[key] = apn[name] || '';
}
}
}
preferences.textContent = JSON.stringify(prefs, true, 2);
}
document.querySelector('form').oninput = update;
loadDB(document.querySelector('textarea'), update);
});