/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); const Ci = Components.interfaces; const Cc = Components.classes; const POSITION_UNAVAILABLE = Ci.nsIDOMGeoPositionError.POSITION_UNAVAILABLE; const SETTING_DEBUG_ENABLED = "geolocation.debugging.enabled"; const SETTING_CHANGED_TOPIC = "mozsettings-changed"; let gLoggingEnabled = false; // if we don't see any wifi responses in 5 seconds, send the request. let gTimeToWaitBeforeSending = 5000; //ms let gWifiScanningEnabled = true; let gWifiResults; let gCellScanningEnabled = false; let gCellResults; function LOG(aMsg) { if (gLoggingEnabled) { aMsg = "*** WIFI GEO: " + aMsg + "\n"; Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(aMsg); dump(aMsg); } } function WifiGeoCoordsObject(lat, lon, acc, alt, altacc) { this.latitude = lat; this.longitude = lon; this.accuracy = acc; this.altitude = alt; this.altitudeAccuracy = altacc; } WifiGeoCoordsObject.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGeoPositionCoords]) }; function WifiGeoPositionObject(lat, lng, acc) { this.coords = new WifiGeoCoordsObject(lat, lng, acc, 0, 0); this.address = null; this.timestamp = Date.now(); } WifiGeoPositionObject.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition]) }; function WifiGeoPositionProvider() { try { gLoggingEnabled = Services.prefs.getBoolPref("geo.wifi.logging.enabled"); } catch (e) {} try { gTimeToWaitBeforeSending = Services.prefs.getIntPref("geo.wifi.timeToWaitBeforeSending"); } catch (e) {} try { gWifiScanningEnabled = Services.prefs.getBoolPref("geo.wifi.scan"); } catch (e) {} try { gCellScanningEnabled = Services.prefs.getBoolPref("geo.cell.scan"); } catch (e) {} this.wifiService = null; this.timeoutTimer = null; this.started = false; } WifiGeoPositionProvider.prototype = { classID: Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationProvider, Ci.nsIWifiListener, Ci.nsITimerCallback, Ci.nsIObserver]), listener: null, observe: function(aSubject, aTopic, aData) { if (aTopic != SETTING_CHANGED_TOPIC) { return; } try { let setting = JSON.parse(aData); if (setting.key != SETTING_DEBUG_ENABLED) { return; } gLoggingEnabled = setting.value; } catch (e) { } }, startup: function() { if (this.started) return; this.started = true; let settingsCallback = { handle: function(name, result) { gLoggingEnabled = result && result.value === true ? true : false; }, handleError: function(message) { gLoggingEnabled = false; LOG("settings callback threw an exception, dropping"); } }; try { Services.obs.addObserver(this, SETTING_CHANGED_TOPIC, false); let settings = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService); settings.createLock().get(SETTING_DEBUG_ENABLED, settingsCallback); } catch(ex) { // This platform doesn't have the settings interface, and that is just peachy } if (gWifiScanningEnabled && Cc["@mozilla.org/wifi/monitor;1"]) { this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Components.interfaces.nsIWifiMonitor); this.wifiService.startWatching(this); } this.timeoutTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this.timeoutTimer.initWithCallback(this, gTimeToWaitBeforeSending, this.timeoutTimer.TYPE_REPEATING_SLACK); LOG("startup called."); }, watch: function(c) { this.listener = c; }, shutdown: function() { LOG("shutdown called"); if (this.started == false) { return; } if (this.timeoutTimer) { this.timeoutTimer.cancel(); this.timeoutTimer = null; } if(this.wifiService) { this.wifiService.stopWatching(this); this.wifiService = null; } Services.obs.removeObserver(this, SETTING_CHANGED_TOPIC); this.listener = null; this.started = false; }, setHighAccuracy: function(enable) { }, onChange: function(accessPoints) { function isPublic(ap) { let mask = "_nomap" let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length); if (result != -1) { LOG("Filtering out " + ap.ssid + " " + result); } return result; }; function sort(a, b) { return b.signal - a.signal; }; function encode(ap) { return { 'macAddress': ap.mac, 'signalStrength': ap.signal }; }; if (accessPoints) { gWifiResults = accessPoints.filter(isPublic).sort(sort).map(encode); } else { gWifiResults = null; } }, onError: function (code) { LOG("wifi error: " + code); }, updateMobileInfo: function() { LOG("updateMobileInfo called"); try { let radioService = Cc["@mozilla.org/ril;1"] .getService(Ci.nsIRadioInterfaceLayer); let numInterfaces = radioService.numRadioInterfaces; let result = []; for (let i = 0; i < numInterfaces; i++) { LOG("Looking for SIM in slot:" + i + " of " + numInterfaces); let radio = radioService.getRadioInterface(i); let iccInfo = radio.rilContext.iccInfo; let cell = radio.rilContext.voice.cell; if (iccInfo && cell) { // TODO type and signal strength result.push({ radio: "gsm", mobileCountryCode: iccInfo.mcc, mobileNetworkCode: iccInfo.mnc, locationAreaCode: cell.gsmLocationAreaCode, cellId: cell.gsmCellId }); } } return result; } catch (e) { gCellResults = null; } }, notify: function (timeoutTimer) { let url = Services.urlFormatter.formatURLPref("geo.wifi.uri"); let listener = this.listener; LOG("Sending request: " + url + "\n"); let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Ci.nsIXMLHttpRequest); listener.locationUpdatePending(); try { xhr.open("POST", url, true); } catch (e) { listener.notifyError(POSITION_UNAVAILABLE); return; } xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8"); xhr.responseType = "json"; xhr.mozBackgroundRequest = true; xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS; xhr.onerror = function() { listener.notifyError(POSITION_UNAVAILABLE); }; xhr.onload = function() { LOG("gls returned status: " + xhr.status + " --> " + JSON.stringify(xhr.response)); if ((xhr.channel instanceof Ci.nsIHttpChannel && xhr.status != 200) || !xhr.response || !xhr.response.location) { listener.notifyError(POSITION_UNAVAILABLE); return; } let newLocation = new WifiGeoPositionObject(xhr.response.location.lat, xhr.response.location.lng, xhr.response.accuracy); listener.update(newLocation); }; if (gCellScanningEnabled) { this.updateMobileInfo(); } let data = {}; if (gWifiResults) { data.wifiAccessPoints = gWifiResults; } if (gCellResults) { data.cellTowers = gCellResults; } data = JSON.stringify(data); gWifiResults = gCellResults = null; LOG("sending " + data); xhr.send(data); }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiGeoPositionProvider]);