VulpesOS/gaia/apps/system/js/radio.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

316 lines
9 KiB
JavaScript
Executable file

/* global BaseModule, MobileConnectionIcon, OperatorIcon, LazyLoader */
'use strict';
(function() {
var Radio = function(core) {
/*
* An internal key used to make sure Radio is
* enabled or not.
*
* @default {Boolean} null
*/
this._enabled = null;
/*
* An internal key used to track how may operations have
* been executed on radio.
*
* @default {Number} 0
*/
this._setRadioOpCount = 0;
/*
* Data connection type mapping result
*/
this.types = [];
this.states = [];
/*
* An internal key used to track whether there is any error
* happened when calling setRadioEnabled
*
* @default {Boolean} false
*/
this._isSetRadioOpError = false;
/*
* An internal variable to cache mozMobileConnections
*/
this._mobileConnections = core.mobileConnections || null;
};
Radio.EVENTS = [
'airplanemode-enabled',
'airplanemode-disabled',
'callschanged'
];
Radio.SETTINGS = [
'operatorResources.data.icon',
'ril.radio.disabled'
];
Radio.STATES = [
'settingEnabled',
'enabled',
'isCDMA',
'getDataConnectionType',
'dataIcon'
];
BaseModule.create(Radio, {
name: 'Radio',
EVENT_PREFIX: 'radio',
dataIcon: null,
isCDMA: function(index) {
return !!this.dataExclusiveCDMATypes[
this.mobileConnections[index].data.type];
},
getDataConnectionType: function(index) {
return this.types[index];
},
'_handle_callschanged': function() {
this.icon && this.icon.updateData();
},
'_handle_airplanemode-enabled': function() {
this.enabled = false;
},
'_handle_airplanemode-disabled': function() {
this.enabled = true;
},
'_observe_ril.radio.disabled': function(value) {
this.settingEnabled = !value;
this.icon && this.icon.update();
},
'_observe_operatorResources.data.icon': function(value) {
var dataIcon = value;
if (!dataIcon) {
return;
}
this.dataIcon = value;
for (var key in value) {
//Change only dataIcon values that actually really know
if (this.mobileDataIconTypes[key]) {
this.mobileDataIconTypes[key] = dataIcon[key];
}
}
this.publish('dataiconchanged', value, true);
},
_onDataChange: function(conn, index) {
this.types[index] = this.mobileDataIconTypes[conn.data.type];
if (this.mobileConnections.length === 1) {
this.operatorIcon.update();
}
this.icon && this.icon.update(index);
this.icon && this.icon.updateData(index);
},
_onVoiceChange: function(conn, index) {
if (this.operatorIcon && this.mobileConnections.length === 1) {
this.operatorIcon.update();
}
this.icon && this.icon.update(index);
},
_onRadioStateChange: function(conn, index) {
this.icon && this.icon.update(index);
this.debug('radiostatechange: [' + index + '] ' + conn.radioState);
this.publish('statechange', {
index: index,
state: conn.radioState
});
},
/*
* An internal function used to make sure current radioState
* is ok to do following operations.
*/
_start: function() {
this.dataExclusiveCDMATypes = {
'evdo0': true, 'evdoa': true, 'evdob': true, // data call only
'1xrtt': true, 'is95a': true, 'is95b': true // data call or voice call
};
/* A mapping table between technology names
we would get from API v.s. the icon we want to show. */
this.mobileDataIconTypes = {
'lte': '4G', // 4G LTE
'ehrpd': '4G', // 4G CDMA
'hspa+': 'H+', // 3.5G HSPA+
'hsdpa': 'H', 'hsupa': 'H', 'hspa': 'H', // 3.5G HSDPA
'evdo0': 'Ev', 'evdoa': 'Ev', 'evdob': 'Ev', // 3G CDMA
'umts': '3G', // 3G
'edge': 'E', // EDGE
'gprs': '2G',
'1xrtt': '1x', 'is95a': '1x', 'is95b': '1x' // 2G CDMA
};
this.service.request('stepReady', '#languages').then(function() {
this.debug('step is resolved.');
LazyLoader.load(['js/roaming_icon.js',
'js/signal_icon.js',
'js/mobile_connection_icon.js']).then(function() {
this._stepReady = true;
this.icon = new MobileConnectionIcon(this);
this.icon.start();
}.bind(this)).catch(function(err) {
console.error(err);
});
}.bind(this));
LazyLoader.load(['js/operator_icon.js']).then(function() {
this.operatorIcon = new OperatorIcon(this);
this.operatorIcon.start();
}.bind(this)).catch(function(err) {
console.error(err);
});
this.mobileConnections.forEach(function(conn, index) {
this.types.push('');
conn.addEventListener('radiostatechange',
this._onRadioStateChange.bind(this, conn, index));
conn.addEventListener('datachange',
this._onDataChange.bind(this, conn, index));
conn.addEventListener('voicechange',
this._onVoiceChange.bind(this, conn, index));
}, this);
this.service.request('AirplaneMode:registerNetwork', 'radio', this);
var airplaneMode = this.service.query('AirplaneMode.isActive');
if (undefined !== airplaneMode) {
this.enabled = !airplaneMode;
}
},
_stop: function() {
this.service.request('AirplaneMode:unregisterNetwork', 'radio', this);
},
/*
* An internal function used to make sure current radioState
* is ok to do following operations.
*
* @param {MozMobileConnection} conn
* @param {Boolean} enabled
*/
_setRadioEnabled: function(conn, enabled, index) {
this.debug(conn.radioState + ' ======> ' + enabled);
if (conn.radioState !== 'enabling' &&
conn.radioState !== 'disabling' &&
conn.radioState !== null) {
this._doSetRadioEnabled(conn, enabled, index);
} else {
var radioStateChangeHandler = (function onchange() {
if (conn.radioState == 'enabling' ||
conn.radioState == 'disabling' ||
conn.radioState == null) {
return;
}
conn.removeEventListener('radiostatechange',
radioStateChangeHandler);
this._doSetRadioEnabled(conn, enabled, index);
}).bind(this);
conn.addEventListener('radiostatechange', radioStateChangeHandler);
}
},
/*
* An internal function to tell Gecko setRadioEnabled
*
* @param {MozMobileConnection} conn
* @param {Boolean} enabled
*/
_doSetRadioEnabled: function(conn, enabled, index) {
this.debug('Real operation to turn ' + (enabled ? 'on' : 'off') +
' for ' + index + ' connection.');
var self = this;
(function() {
var req = conn.setRadioEnabled(enabled);
req.onsuccess = function() {
self._setRadioOpCount++;
self._setRadioAfterReqsCalled(enabled);
};
req.onerror = function() {
self.debug('toggle connection ' + index + ' error.');
self._isSetRadioOpError = true;
self._setRadioOpCount++;
self._setRadioAfterReqsCalled(enabled);
};
}());
},
/*
* We have to make sure all mobileConnections work
* as what we have expected and dispatch event to
* tell AirplaneMode that Radio operations are done.
*
* @param {Boolean} enabled
*/
_setRadioAfterReqsCalled: function(enabled) {
if (this._isSetRadioOpError) {
throw new Error('We got error when disabling radio');
}
if (this._setRadioOpCount !== this.mobileConnections.length) {
this.debug('operation not completed yet.', this._setRadioOpCount);
return;
} else {
this._enabled = enabled;
var evtName = enabled ?
'-enabled' : '-disabled';
this.publish(evtName);
}
}
}, {
/*
* We can use this value to know Radio is enabled or not
*
* @return {Boolean}
*/
enabled: {
congfigurable: false,
get: function() {
return this._enabled;
},
/*
* We can set this value to tell Radio service turn on / off
* radio.
*
* @param {Boolean} value
*/
set: function(value) {
this.debug(this._enabled + ' => ' + value);
if (value !== this._enabled) {
this._setRadioOpCount = 0;
this._isSetRadioOpError = false;
this.mobileConnections.forEach(function(conn, index) {
this._setRadioEnabled(conn, value, index);
}, this);
}
}
},
/*
* An internal helper to make mobileConnections iterable
*/
mobileConnections: {
congfigurable: false,
get: function() {
if (!this._mozMobileConnections) {
this._mozMobileConnections =
Array.prototype.slice.call(this._mobileConnections);
}
return this._mozMobileConnections;
}
}
});
})();