VulpesOS/overrides/system.gaiamobile.org/js/wifi.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

441 lines
16 KiB
JavaScript

/* global LazyLoader, Service, SettingsListener,
WifiIcon, WifiWakeLockManager */
'use strict';
(function(exports) {
var Wifi = {
name: 'Wifi',
// Sync with mozSettings wifi.enabled key with an observer.
// Note that there is a timing difference with _enabled and enabled.
wifiEnabled: true,
// True if we have disabled the wifi so we would remember to quietly turn
// it back on.
// (TODO: this should be named wifiDisabledByTimeout)
wifiDisabledByWakelock: false,
// Without an wifi wake lock, wait for screenOffTimeout milliseconds
// to turn wifi off after the conditions are met.
// If it's set to 0, wifi will never be turn off.
// This is in sync with mozSettings wifi.screen_off_timeout.
screenOffTimeout: 0,
// When wifiSleepMode is true and the screenOffTimeout is not zero,
// Wi-Fi will be automatically turned off during sleep to save battery
// power.
// This is in sync with mozSettings wifi.sleepMode.
wifiSleepMode: false,
// if Wifi is enabled but disconnected, try to scan for networks every
// kScanInterval ms.
kScanInterval: 20 * 1000,
_scanTimer: null,
// Sync with the hardware (mozWifiManager) state by listening to the
// two events.
_enabled: false,
get enabled() {
return this._enabled;
},
// true if there is an alarm or if there is an alarm being set.
_hasAlarm: false,
// keep the id returned by mozAlarms API
_alarmId: null,
start: function() {
if (!window.navigator.mozSettings) {
console.error('Wifi: No mozSettings!');
return;
}
if (!window.navigator.mozWifiManager) {
console.error('Wifi: No mozWifiManager!');
return;
}
this.wifiManager = window.navigator.mozWifiManager;
LazyLoader.load(['js/captive_portal.js']);
Service.request('stepReady', '#wifi').then(function() {
return LazyLoader.load(['js/wifi_icon.js']);
}.bind(this)).then(function() {
this.icon = new WifiIcon(this);
this.icon.start();
}.bind(this))['catch'](function(err) { // XXX: workaround gjslint
console.error(err);
});
window.addEventListener('screenchange', this);
var battery = window.navigator.battery;
battery.addEventListener('chargingchange', this);
// If wifi is turned off by us and phone got rebooted,
// bring wifi back.
var name = 'wifi.disabled_by_wakelock';
var req = SettingsListener.getSettingsLock().get(name);
req.onsuccess = function gotWifiDisabledByWakelock() {
if (!req.result[name]) {
return;
}
// Re-enable wifi and reset wifi.disabled_by_wakelock
// SettingsListener.getSettingsLock() always return invalid lock
// in our usage here.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=793239
var lock = navigator.mozSettings.createLock();
lock.set({ 'wifi.enabled': true });
lock.set({ 'wifi.disabled_by_wakelock': false });
};
this._wakeLockManager = new WifiWakeLockManager();
this._wakeLockManager.onwakelockchange = this.maybeToggleWifi.bind(this);
this._wakeLockManager.start();
var self = this;
var wifiManager = window.navigator.mozWifiManager;
// when wifi is really enabled, emit event to notify QuickSettings
wifiManager.onenabled = function onWifiEnabled() {
self._enabled = true;
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent('wifi-enabled',
/* canBubble */ true, /* cancelable */ false, null);
window.dispatchEvent(evt);
self.icon && self.icon.update();
};
// when wifi is really disabled, emit event to notify QuickSettings
wifiManager.ondisabled = function onWifiDisabled() {
this._enabled = false;
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent('wifi-disabled',
/* canBubble */ true, /* cancelable */ false, null);
window.dispatchEvent(evt);
if (self.icon) {
self.icon.update();
}
};
// when wifi status change, emit event to notify Statusbar/UpdateManager
wifiManager.onstatuschange = function onWifiDisabled() {
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent('wifi-statuschange',
/* canBubble */ true, /* cancelable */ false, null);
window.dispatchEvent(evt);
self.icon && self.icon.update();
};
wifiManager.onconnectioninfoupdate = function() {
self.icon && self.icon.update();
};
SettingsListener.observe(
'wifi.screen_off_timeout', 600000, function(value) {
self.screenOffTimeout = value;
});
SettingsListener.observe('wifi.sleepMode', true, function(value) {
self.wifiSleepMode = value;
});
Service.registerState('enabled', this);
// Track the wifi.enabled mozSettings value
SettingsListener.observe('wifi.enabled', true, function(value) {
if (!wifiManager && value) {
self.wifiEnabled = false;
// roll back the setting value to notify the UIs
// that wifi interface is not available
if (value) {
SettingsListener.getSettingsLock().set({
'wifi.enabled': false
});
}
return;
}
self.wifiEnabled = value;
if (self.icon) {
self.icon && self.icon.update();
}
clearTimeout(self._scanTimer);
if (!value) {
return;
}
// If wifi is enabled but disconnected.
// we would need to call getNetworks() continuously
// so we could join known wifi network
self._scanTimer = setInterval(function wifi_scan() {
if (wifiManager.connection.status == 'disconnected') {
wifiManager.getNetworks();
}
}, self.kScanInterval);
});
},
handleEvent: function wifi_handleEvent(evt) {
this.maybeToggleWifi();
},
/**
* Check the status of screen, wifi wake lock, and power source
* and turn on/off wifi accordingly.
*
* You can think of this as a gigantic state machine where the input
* is the states and the output is triggering the timer to turn wifi off,
* or turning the wifi back on (if it is turned off by us).
*
* |--------------------------------|---------------------|
* | Conditions | |
* |--------|-----------|-----------| End state |
* | Screen | Power | Wake lock | |
* |--------|-----------|-----------|---------------------|
* | off | unplugged | not hold | set timer to off (1)|
* |--------|-----------|-----------|---------------------|
* | off | unplugged | hold | turn back on |
* | | | | if turned off by |
* | | | | timer (2) |
* |--------|-----------|-----------|---------------------|
* | off | plugged | not hold | turn back on |
* | | | | if turned off by |
* | | | | timer (3) |
* |--------|-----------|-----------|---------------------|
* | off | plugged | hold | turn back on |
* | | | | if turned off by |
* | | | | timer (4) |
* |--------|-----------|-----------|---------------------|
* | on | unplugged | not hold | turn back on |
* | | | | if turned off by |
* | | | | timer (5) |
* |--------|-----------|-----------|---------------------|
* | on | unplugged | hold | turn back on |
* | | | | if turned off by |
* | | | | timer (6) |
* |--------|-----------|-----------|---------------------|
* | on | plugged | not hold | turn back on |
* | | | | if turned off by |
* | | | | timer (7) |
* |--------|-----------|-----------|---------------------|
* | on | plugged | hold | turn back on |
* | | | | if turned off by |
* | | | | timer (8) |
* |--------|-----------|-----------|---------------------|
*
* The rest of the code are housekeepers making sure we keep an CPU lock
* during the operations, and try not to set the timer twice etc.
*
* It is also assumed setting mozSettings w/ the same value would be a
* no-op with no side effects so we don't really protect that.
*
* As the entry point to of the state machine, it is very important to
* keep this function making the right decisions at all times.
*/
maybeToggleWifi: function wifi_maybeToggleWifi() {
// Keep CPU awake to ensure the flow is done.
var cpuLock = navigator.requestWakeLock('cpu');
var releaseCpuLock = function() {
if (cpuLock) {
cpuLock.unlock();
cpuLock = null;
}
if (timeoutId) {
window.clearTimeout(timeoutId);
timeoutId = 0;
}
};
// To prevent the CPU awake forever (if anything goes run)
var timeoutId = window.setTimeout(releaseCpuLock, 30000);
// Do nothing if we are being disabled.
if (!this.screenOffTimeout) {
releaseCpuLock();
return;
}
var battery = window.navigator.battery;
var wifiManager = window.navigator.mozWifiManager;
if (!battery || !wifiManager ||
// We don't need to do anything if wifi is not disabled by system app.
(!this.wifiEnabled && !this.wifiDisabledByWakelock)) {
releaseCpuLock();
return;
}
var lock = SettingsListener.getSettingsLock();
// Let's quietly turn off wifi if there is no wake lock and
// the screen is off and we are not on a power source.
if (!Service.query('screenEnabled') && !battery.charging &&
!this._wakeLockManager.isHeld) {
// We still need to turn of wifi even if there is no Alarm API
if (!navigator.mozAlarms) {
console.warn('Turning off wifi without sleep timer because' +
' Alarm API is not available');
this.sleep();
releaseCpuLock();
return;
}
// Set System Message Handler,
// so we will be notified when alarm goes off.
this.setSystemMessageHandler();
// When user wants to allow wifi off then start with a timer,
// only turn off wifi till timeout.
if (this.wifiSleepMode === true) {
// maybeToggleWifi state machine state #1
// We should not try to set more than one alarms.
if (this._hasAlarm) {
releaseCpuLock();
return;
}
this._hasAlarm = true;
var date = new Date(Date.now() + this.screenOffTimeout);
var req = navigator.mozAlarms.add(date, 'ignoreTimezone', 'wifi-off');
req.onsuccess = function wifi_offAlarmSet() {
this._alarmId = req.result;
releaseCpuLock();
}.bind(this);
req.onerror = function wifi_offAlarmSetFailed() {
console.warn('Fail to set wifi sleep timer on Alarm API. ' +
'Turn off wifi immediately.');
this._hasAlarm = false;
this.sleep();
releaseCpuLock();
}.bind(this);
} else {
releaseCpuLock();
return;
}
}
// ... and quietly turn it back on or cancel the timer otherwise
else {
// maybeToggleWifi state machine state #2 to #8
if (this._alarmId) {
navigator.mozAlarms.remove(this._alarmId);
this._hasAlarm = false;
this._alarmId = null;
}
// If wifi is enabled but disconnected.
// we would need to call getNetworks()
// so we could join known wifi network
if (this.wifiEnabled &&
wifiManager.connection.status == 'disconnected') {
wifiManager.getNetworks();
}
// We don't need to do anything if
// we didn't disable wifi at first place.
if (!this.wifiDisabledByWakelock) {
releaseCpuLock();
return;
}
this.wifiDisabledByWakelock = false;
if (this.wifiEnabled) {
releaseCpuLock();
return;
}
// turn wifi back on.
lock.set({ 'wifi.enabled': true });
lock.set({ 'wifi.disabled_by_wakelock': false });
window.addEventListener('wifi-enabled', function() {
releaseCpuLock();
});
}
},
// Quietly turn off wifi for real, set wifiDisabledByWakelock to true
// so we will turn it back on.
sleep: function wifi_sleep() {
// The |sleep| might be triggered when an alarm comes.
// If the CPU is in suspend mode at this moment, alarm servcie would wake
// up the CPU to run the handler and turn it back to suspend immediately
// |sleep| is finished.
// In this case, we acquire a CPU wake lock to prevent
// the CPU goes to suspend mode before the switching is done.
var wakeLockForWifi = navigator.requestWakeLock('cpu');
var releaseWakeLockForWifi = function() {
if (wakeLockForWifi) {
wakeLockForWifi.unlock();
wakeLockForWifi = null;
}
if (timeoutId) {
window.clearTimeout(timeoutId);
timeoutId = 0;
}
};
// To prevent the CPU awake forever (if wifi cannot be disabled)
var timeoutId = window.setTimeout(releaseWakeLockForWifi, 30000);
// Remember that it was turned off by us.
this.wifiDisabledByWakelock = true;
var request = null;
var lock = SettingsListener.getSettingsLock();
// Actually turn off the wifi
var wakeLockForSettings = navigator.requestWakeLock('cpu');
lock.set({ 'wifi.enabled': false });
window.addEventListener('wifi-disabled', releaseWakeLockForWifi);
// Keep this value in disk so if the phone reboots we'll
// be able to turn the wifi back on.
request = lock.set({ 'wifi.disabled_by_wakelock': true });
request.onsuccess = function() { wakeLockForSettings.unlock(); };
request.onerror = request.onsuccess;
},
// Register for handling system message,
// this cannot be done during |start()| because of bug 797803
setSystemMessageHandler: function wifi_setSystemMessageHandler() {
if (this._systemMessageHandlerRegistered) {
return;
}
this._systemMessageHandlerRegistered = true;
navigator.mozSetMessageHandler('alarm', function gotAlarm(message) {
if (message.data !== 'wifi-off') {
return;
}
this._hasAlarm = false;
this._alarmId = null;
// Wifi shall not sleep if the condition to turn this off has changed.
// (Alarm should have been removed when condition changes, but for
// reason unknown to us it still triggers.)
var battery = window.navigator.battery;
if (Service.query('screenEnabled') ||
battery.charging ||
this._wakeLockManager.isHeld) {
console.error(
'Wifi: mozAlarms should be cancelled but still triggers.');
return;
}
this.sleep();
}.bind(this));
}
};
exports.Wifi = Wifi;
}(window));