/** * WifiContext is a singleton that you can easily use it to fetch * some shared data across different panels * * @module WifiContext */ define(function(require) { 'use strict'; // modules / helpers var SettingsCache = require('modules/settings_cache'); var WifiHelper = require('shared/wifi_helper'); var wifiManager = WifiHelper.getWifiManager(); var settings = navigator.mozSettings; // observed objects var _currentNetwork = wifiManager && wifiManager.connection && wifiManager.connection.network; var WifiContext = { /** * These listeners would be called when wifi is enabled * * @memberOf WifiContext * @type {Array} */ _wifiEnabledListeners: [], /** * These listeners would be called when wifi is disabled * * @memberOf WifiContext * @type {Array} */ _wifiDisabledListeners: [], /** * These listeners would be called when wifi is changed * * @memberOf WifiContext * @type {Array} */ _wifiStatusChangeListeners: [], /** * These listeners would be called when * 1. localized * 2. wifi is enabled * 3. wifi is changed * 4. wifi is disabled * * @memberOf WifiContext * @type {Array} */ _wifiStatusTextChangeListeners: [], /** * These listeners would be called when the current wifi * information is updated * * @memberOf WifiContext * @type {Array} */ _wifiConnectionInfoUpdateListeners: [], /** * These listeners would be called when the wifi network * is forgotten. * * @memberOf WifiContext * @type {Array} */ _wifiNetworkForgottenListeners: [], /** * These listeners would be called when * 1. wifi connection failed * * @memberOf WifiContext * @type {Array} */ _wifiWrongPasswordListeners: [], /** * Desc about customized wifi status * * @memberOf WifiContext * @type {String} */ _wifiStatusText: { id: null }, /** * Mac address * * @memberOf WifiContext * @type {String} */ _macAddress: '', /** * Init is used to initialize some basic stuffs * * @memberOf WifiContext */ _init: function() { if (!wifiManager) { return; } // Set wifi.enabled so that it mirrors the state of the hardware. // wifi.enabled is not an ordinary user setting because the system // turns it on and off when wifi goes up and down. settings.createLock().set({'wifi.enabled': wifiManager.enabled}); this._bindEvents(); // we would call _updateWifi() when init this._updateWifi(); }, /** * We will bind some default listeners here * * @memberOf WifiContext */ _bindEvents: function() { var self = this; var _updateWifi = this._updateWifi.bind(this); var _updateNetworkStatus = this._updateNetworkStatus.bind(this); // make sure we would update anything when wifi got changed this._wifiEnabledListeners.push(_updateWifi, _updateNetworkStatus); this._wifiDisabledListeners.push(_updateWifi); this._wifiStatusChangeListeners.push(_updateWifi, _updateNetworkStatus); // Now register callbacks to track the state of the wifi hardware if (wifiManager) { wifiManager.onenabled = function(event) { self._wifiEnabled(event); self._wifiStatusTextChange(); }; wifiManager.ondisabled = function(event) { self._wifiDisabled(event); self._wifiStatusTextChange(); }; wifiManager.onstatuschange = function(event) { self._wifiStatusChange.call(self, event); self._wifiStatusTextChange(); }; wifiManager.onconnectioninfoupdate = self._wifiConnectionInfoUpdate.bind(self); } }, /** * We will update mac address, wifiStatus when wifi is updated * * @memberOf WifiContext */ _updateWifi: function(event) { var self = this; if (!wifiManager) { return; } // If the MAC address is in the Settings database, it's already // displayed in all `MAC address' fields; if not, it will be set as soon // as the Wi-Fi is enabled (see `storeMacAddress'). if (!this._macAddress && settings) { SettingsCache.getSettings(function(results) { self._macAddress = results['deviceinfo.mac']; }); } if (wifiManager.enabled) { this._storeMacAddress(); } // reflect latest value of wifiStatus this._updateWifiStatusText(event); }, /** * We will update mac address, wifiStatus when wifi is updated * * @memberOf WifiContext */ _updateWifiStatusText: function(event) { if (wifiManager.enabled) { var network = (event && event.network) ? event.network : wifiManager.connection.network; var status = event && event.status ? event.status : wifiManager.connection.status; var networkProp = network ? {ssid: network.ssid} : null; this._wifiStatusText = { id: 'full-status-' + status, args: networkProp }; } else { this._wifiStatusText = { id: 'disabled' }; } }, /** * We need to handle network status when wifi is enabled / wifistatus * is changed. * * _currentNetwork: user activated current network * wifiManager.connection.network: system activated current network * * Main difference between |_currentNetwork| and |wifiManager.connection. * network| is that we can't tell if network is known while connecting by * |wifiManager.connectiohn.network.known| because it will always be true. * * Instead, we use |_currentNetwork| to indicate if current connecting * network is new or known network. * * When system connect to network by user's tap on UI, |_currentNetwork| * will point to same network as |wifiManager.connection.network|. * * On the other hand, if system connect to network automatically under * conditions like wifi enable, move into AP's range, or reconnect to * other network after delete current connected network, |_currentNetwork| * will be null, while |wifiManager.connection.network| contains * information about current network. * * @memberOf WifiContext */ _updateNetworkStatus: function(event) { if (!_currentNetwork) { return; } if (event && event.network && _currentNetwork.ssid != event.network.ssid) { return; } var networkStatus = event ? event.status : wifiManager.connection.status; if (networkStatus === 'connectingfailed') { // Since this.forgetNetwork will set _currentNetwork to null, we have // to save the _currentNetwork.known first. var isCurrentNetworkKnown = _currentNetwork.known; delete _currentNetwork.password; this.forgetNetwork(_currentNetwork); if (isCurrentNetworkKnown === false) { // Connection fail on user-activated unknown network, should be wrong // password, delete network and force a new authentication dialog. this._wifiWrongPassword(); } } }, /** * When wifi is enabled, we will make sure the mozSettings is sync * with hardward status and call all registered listeners. * * @memberOf WifiContext */ _wifiEnabled: function(event) { var self = this; this._syncWifiEnabled(true, function() { self._wifiEnabledListeners.forEach(function(listener) { listener(event); }); }); }, /** * When wifi is disabled, we will make sure the mozSettings is sync * with hardward status and call all registered listeners. * * @memberOf WifiContext */ _wifiDisabled: function(event) { var self = this; this._syncWifiEnabled(false, function() { self._wifiDisabledListeners.forEach(function(listener) { listener(event); }); }); }, /** * When wifi's status is changed, we will call all registered listeners. * * @memberOf WifiContext */ _wifiStatusChange: function(event) { this._wifiStatusChangeListeners.forEach(function(listener) { listener(event); }); }, /** * When text of wifiStatus is changed, we will call all registered * listeners. * * @memberOf WifiContext */ _wifiStatusTextChange: function() { this._wifiStatusTextChangeListeners.forEach(function(listener) { listener(); }); }, /** * When wifi connection fails, we will call all registered * listeners. * * @memberOf WifiContext */ _wifiWrongPassword: function() { this._wifiWrongPasswordListeners.forEach(function(listener) { listener(); }); }, /** * When wifi's connection is updated, we will call all registered listeners. * * @memberOf WifiContext */ _wifiConnectionInfoUpdate: function(event) { this._wifiConnectionInfoUpdateListeners.forEach(function(listener) { listener(event); }); }, /** * When wifi's network is forgotten, we will call all registered listeners. * * @memberOf WifiContext */ _wifiNetworkForgotten: function(event) { this._wifiNetworkForgottenListeners.forEach(function(listener) { listener(event); }); }, /** * Keep mac address in mozSettings * * @memberOf WifiContext */ _storeMacAddress: function() { if (!wifiManager) { return; } // Store the MAC address in the Settings database. Note: the wifiManager // sets macAddress to the string `undefined' when it is not available. if (settings && wifiManager.macAddress && wifiManager.macAddress !== this._macAddress && wifiManager.macAddress !== 'undefined') { this._macAddress = wifiManager.macAddress; settings.createLock().set({ 'deviceinfo.mac': this._macAddress }); } }, /** * Keep the setting in sync with the hardware state. We need to do this * because b2g/dom/wifi/WifiWorker.js can turn the hardware on and off. * * @memberOf WifiContext * @param {Boolean} enabled * @param {Function} callback */ _syncWifiEnabled: function(enabled, callback) { SettingsCache.getSettings(function(results) { var wifiEnabled = results['wifi.enabled']; if (wifiEnabled !== enabled) { settings.createLock().set({'wifi.enabled': enabled}); } callback(); }); }, /** * This is an internal function that can help us find out the matched * callback from catched listeners and remove it * * @memberOf WifiContext * @param {Array} listeners * @param {Function} callback */ _removeEventListener: function(listeners, callback) { var index = listeners.indexOf(callback); if (index >= 0) { listeners.splice(index, 1); } }, /** * This is a wrapper of WifiManger.associate to make sure we can handle * our internal status at the same time. * * @memberOf WifiContext * @param {Object} network * @param {Function} callback */ associateNetwork: function(network, cb) { cb = cb || function() {}; var request = wifiManager.associate(network); var done = function() { if (!request.error) { _currentNetwork = network; } cb(request.error); }; request.onsuccess = done; request.onerror = done; }, /** * This is a wrapper of WifiManger.forget to make sure we can handle * our internal status at the same time. * * @memberOf WifiContext * @param {Object} network * @param {Function} callback */ forgetNetwork: function(network, cb) { cb = cb || function() {}; var request = wifiManager.forget(network); var done = function() { if (!request.error) { _currentNetwork = null; WifiContext._wifiNetworkForgotten({ network: network }); } cb(request.error); }; request.onsuccess = done; request.onerror = done; } }; WifiContext._init(); return { addEventListener: function(eventName, callback) { if (eventName === 'wifiEnabled') { WifiContext._wifiEnabledListeners.push(callback); } else if (eventName === 'wifiDisabled') { WifiContext._wifiDisabledListeners.push(callback); } else if (eventName === 'wifiStatusChange') { WifiContext._wifiStatusChangeListeners.push(callback); } else if (eventName === 'wifiStatusTextChange') { WifiContext._wifiStatusTextChangeListeners.push(callback); } else if (eventName === 'wifiWrongPassword') { WifiContext._wifiWrongPasswordListeners.push(callback); } else if (eventName === 'wifiConnectionInfoUpdate') { WifiContext._wifiConnectionInfoUpdateListeners.push(callback); } else if (eventName === 'wifiNetworkForgotten') { WifiContext._wifiNetworkForgottenListeners.push(callback); } }, removeEventListener: function(eventName, callback) { if (eventName === 'wifiEnabled') { WifiContext._removeEventListener( WifiContext._wifiEnabledListeners, callback); } else if (eventName === 'wifiDisabled') { WifiContext._removeEventListener( WifiContext._wifiDisabledListeners, callback); } else if (eventName === 'wifiStatusChange') { WifiContext._removeEventListener( WifiContext._wifiStatusChangeListeners, callback); } else if (eventName === 'wifiStatusTextChange') { WifiContext._removeEventListener( WifiContext._wifiStatusTextChangeListeners, callback); } else if (eventName === 'wifiWrongPassword') { WifiContext._removeEventListener( WifiContext._wifiWrongPasswordListeners, callback); } else if (eventName === 'wifiConnectionInfoUpdate') { WifiContext._removeEventListener( WifiContext._wifiConnectionInfoUpdateListeners, callback); } else if (eventName === 'wifiNetworkForgotten') { WifiContext._removeEventListener( WifiContext._wifiNetworkForgottenListeners, callback); } }, get wifiStatusText() { return WifiContext._wifiStatusText; }, get currentNetwork() { return _currentNetwork; }, forgetNetwork: WifiContext.forgetNetwork, associateNetwork: WifiContext.associateNetwork }; });