import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,489 @@
/* 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/. */
"use strict";
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 Cu = Components.utils;
const POSITION_UNAVAILABLE = Ci.nsIDOMGeoPositionError.POSITION_UNAVAILABLE;
const SETTINGS_DEBUG_ENABLED = "geolocation.debugging.enabled";
const SETTINGS_CHANGED_TOPIC = "mozsettings-changed";
const SETTINGS_WIFI_ENABLED = "wifi.enabled";
var gLoggingEnabled = false;
/*
The gLocationRequestTimeout controls how long we wait on receiving an update
from the Wifi subsystem. If this timer fires, we believe the Wifi scan has
had a problem and we no longer can use Wifi to position the user this time
around (we will continue to be hopeful that Wifi will recover).
This timeout value is also used when Wifi scanning is disabled (see
gWifiScanningEnabled). In this case, we use this timer to collect cell/ip
data and xhr it to the location server.
*/
var gLocationRequestTimeout = 5000;
var gWifiScanningEnabled = true;
function LOG(aMsg) {
if (gLoggingEnabled) {
aMsg = "*** WIFI GEO: " + aMsg + "\n";
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(aMsg);
dump(aMsg);
}
}
function CachedRequest(loc, cellInfo, wifiList) {
this.location = loc;
let wifis = new Set();
if (wifiList) {
for (let i = 0; i < wifiList.length; i++) {
wifis.add(wifiList[i].macAddress);
}
}
// Use only these values for equality
// (the JSON will contain additional values in future)
function makeCellKey(cell) {
return "" + cell.radio + ":" + cell.mobileCountryCode + ":" +
cell.mobileNetworkCode + ":" + cell.locationAreaCode + ":" +
cell.cellId;
}
let cells = new Set();
if (cellInfo) {
for (let i = 0; i < cellInfo.length; i++) {
cells.add(makeCellKey(cellInfo[i]));
}
}
this.hasCells = () => cells.size > 0;
this.hasWifis = () => wifis.size > 0;
// if fields match
this.isCellEqual = function(cellInfo) {
if (!this.hasCells()) {
return false;
}
let len1 = cells.size;
let len2 = cellInfo.length;
if (len1 != len2) {
LOG("cells not equal len");
return false;
}
for (let i = 0; i < len2; i++) {
if (!cells.has(makeCellKey(cellInfo[i]))) {
return false;
}
}
return true;
};
// if 50% of the SSIDS match
this.isWifiApproxEqual = function(wifiList) {
if (!this.hasWifis()) {
return false;
}
// if either list is a 50% subset of the other, they are equal
let common = 0;
for (let i = 0; i < wifiList.length; i++) {
if (wifis.has(wifiList[i].macAddress)) {
common++;
}
}
let kPercentMatch = 0.5;
return common >= (Math.max(wifis.size, wifiList.length) * kPercentMatch);
};
this.isGeoip = function() {
return !this.hasCells() && !this.hasWifis();
};
this.isCellAndWifi = function() {
return this.hasCells() && this.hasWifis();
};
this.isCellOnly = function() {
return this.hasCells() && !this.hasWifis();
};
this.isWifiOnly = function() {
return this.hasWifis() && !this.hasCells();
};
}
var gCachedRequest = null;
var gDebugCacheReasoning = ""; // for logging the caching logic
// This function serves two purposes:
// 1) do we have a cached request
// 2) is the cached request better than what newCell and newWifiList will obtain
// If the cached request exists, and we know it to have greater accuracy
// by the nature of its origin (wifi/cell/geoip), use its cached location.
//
// If there is more source info than the cached request had, return false
// In other cases, MLS is known to produce better/worse accuracy based on the
// inputs, so base the decision on that.
function isCachedRequestMoreAccurateThanServerRequest(newCell, newWifiList)
{
gDebugCacheReasoning = "";
let isNetworkRequestCacheEnabled = true;
try {
// Mochitest needs this pref to simulate request failure
isNetworkRequestCacheEnabled = Services.prefs.getBoolPref("geo.wifi.debug.requestCache.enabled");
if (!isNetworkRequestCacheEnabled) {
gCachedRequest = null;
}
} catch (e) {}
if (!gCachedRequest || !isNetworkRequestCacheEnabled) {
gDebugCacheReasoning = "No cached data";
return false;
}
if (!newCell && !newWifiList) {
gDebugCacheReasoning = "New req. is GeoIP.";
return true;
}
if (newCell && newWifiList && (gCachedRequest.isCellOnly() || gCachedRequest.isWifiOnly())) {
gDebugCacheReasoning = "New req. is cell+wifi, cache only cell or wifi.";
return false;
}
if (newCell && gCachedRequest.isWifiOnly()) {
// In order to know if a cell-only request should trump a wifi-only request
// need to know if wifi is low accuracy. >5km would be VERY low accuracy,
// it is worth trying the cell
var isHighAccuracyWifi = gCachedRequest.location.coords.accuracy < 5000;
gDebugCacheReasoning = "Req. is cell, cache is wifi, isHigh:" + isHighAccuracyWifi;
return isHighAccuracyWifi;
}
let hasEqualCells = false;
if (newCell) {
hasEqualCells = gCachedRequest.isCellEqual(newCell);
}
let hasEqualWifis = false;
if (newWifiList) {
hasEqualWifis = gCachedRequest.isWifiApproxEqual(newWifiList);
}
gDebugCacheReasoning = "EqualCells:" + hasEqualCells + " EqualWifis:" + hasEqualWifis;
if (gCachedRequest.isCellOnly()) {
gDebugCacheReasoning += ", Cell only.";
if (hasEqualCells) {
return true;
}
} else if (gCachedRequest.isWifiOnly() && hasEqualWifis) {
gDebugCacheReasoning +=", Wifi only."
return true;
} else if (gCachedRequest.isCellAndWifi()) {
gDebugCacheReasoning += ", Cache has Cell+Wifi.";
if ((hasEqualCells && hasEqualWifis) ||
(!newWifiList && hasEqualCells) ||
(!newCell && hasEqualWifis))
{
return true;
}
}
return false;
}
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 {
gLocationRequestTimeout = Services.prefs.getIntPref("geo.wifi.timeToWaitBeforeSending");
} catch (e) {}
try {
gWifiScanningEnabled = Services.prefs.getBoolPref("geo.wifi.scan");
} catch (e) {}
this.wifiService = null;
this.timer = 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 != SETTINGS_CHANGED_TOPIC) {
return;
}
try {
if ("wrappedJSObject" in aSubject) {
aSubject = aSubject.wrappedJSObject;
}
if (aSubject.key == SETTINGS_DEBUG_ENABLED) {
gLoggingEnabled = aSubject.value;
} else if (aSubject.key == SETTINGS_WIFI_ENABLED) {
gWifiScanningEnabled = aSubject.value;
}
} catch (e) {
}
},
resetTimer: function() {
if (this.timer) {
this.timer.cancel();
this.timer = null;
}
// wifi thread triggers WifiGeoPositionProvider to proceed, with no wifi, do manual timeout
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback(this,
gLocationRequestTimeout,
this.timer.TYPE_REPEATING_SLACK);
},
startup: function() {
if (this.started)
return;
this.started = true;
let self = this;
let settingsCallback = {
handle: function(name, result) {
// Stop the B2G UI setting from overriding the js prefs setting, and turning off logging
// If gLoggingEnabled is already on during startup, that means it was set in js prefs.
if (name == SETTINGS_DEBUG_ENABLED && !gLoggingEnabled) {
gLoggingEnabled = result;
} else if (name == SETTINGS_WIFI_ENABLED) {
gWifiScanningEnabled = result;
if (self.wifiService) {
self.wifiService.stopWatching(self);
}
if (gWifiScanningEnabled) {
self.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Ci.nsIWifiMonitor);
self.wifiService.startWatching(self);
}
}
},
handleError: function(message) {
gLoggingEnabled = false;
LOG("settings callback threw an exception, dropping");
}
};
Services.obs.addObserver(this, SETTINGS_CHANGED_TOPIC, false);
let settingsService = Cc["@mozilla.org/settingsService;1"];
if (settingsService) {
let settings = settingsService.getService(Ci.nsISettingsService);
settings.createLock().get(SETTINGS_WIFI_ENABLED, settingsCallback);
settings.createLock().get(SETTINGS_DEBUG_ENABLED, settingsCallback);
}
if (gWifiScanningEnabled && Cc["@mozilla.org/wifi/monitor;1"]) {
if (this.wifiService) {
this.wifiService.stopWatching(this);
}
this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Ci.nsIWifiMonitor);
this.wifiService.startWatching(this);
}
this.resetTimer();
LOG("startup called.");
},
watch: function(c) {
this.listener = c;
},
shutdown: function() {
LOG("shutdown called");
if (this.started == false) {
return;
}
// Without clearing this, we could end up using the cache almost indefinitely
// TODO: add logic for cache lifespan, for now just be safe and clear it
gCachedRequest = null;
if (this.timer) {
this.timer.cancel();
this.timer = null;
}
if(this.wifiService) {
this.wifiService.stopWatching(this);
this.wifiService = null;
}
Services.obs.removeObserver(this, SETTINGS_CHANGED_TOPIC);
this.listener = null;
this.started = false;
},
setHighAccuracy: function(enable) {
},
onChange: function(accessPoints) {
// we got some wifi data, rearm the timer.
this.resetTimer();
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 false;
}
return true;
};
function sort(a, b) {
return b.signal - a.signal;
};
function encode(ap) {
return { 'macAddress': ap.mac, 'signalStrength': ap.signal };
};
let wifiData = null;
if (accessPoints) {
wifiData = accessPoints.filter(isPublic).sort(sort).map(encode);
}
this.sendLocationRequest(wifiData);
},
onError: function (code) {
LOG("wifi error: " + code);
this.sendLocationRequest(null);
},
notify: function (timer) {
this.sendLocationRequest(null);
},
sendLocationRequest: function (wifiData) {
let data = { cellTowers: undefined, wifiAccessPoints: undefined };
if (wifiData && wifiData.length >= 2) {
data.wifiAccessPoints = wifiData;
}
let useCached = isCachedRequestMoreAccurateThanServerRequest(data.cellTowers,
data.wifiAccessPoints);
LOG("Use request cache:" + useCached + " reason:" + gDebugCacheReasoning);
if (useCached) {
gCachedRequest.location.timestamp = Date.now();
this.notifyListener("update", [gCachedRequest.location]);
return;
}
// From here on, do a network geolocation request //
let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
LOG("Sending request");
let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
this.notifyListener("locationUpdatePending");
try {
xhr.open("POST", url, true);
xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
} catch (e) {
this.notifyListener("notifyError",
[POSITION_UNAVAILABLE]);
return;
}
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xhr.responseType = "json";
xhr.mozBackgroundRequest = true;
xhr.timeout = Services.prefs.getIntPref("geo.wifi.xhr.timeout");
xhr.ontimeout = (function() {
LOG("Location request XHR timed out.")
this.notifyListener("notifyError",
[POSITION_UNAVAILABLE]);
}).bind(this);
xhr.onerror = (function() {
this.notifyListener("notifyError",
[POSITION_UNAVAILABLE]);
}).bind(this);
xhr.onload = (function() {
LOG("server returned status: " + xhr.status + " --> " + JSON.stringify(xhr.response));
if ((xhr.channel instanceof Ci.nsIHttpChannel && xhr.status != 200) ||
!xhr.response || !xhr.response.location) {
this.notifyListener("notifyError",
[POSITION_UNAVAILABLE]);
return;
}
let newLocation = new WifiGeoPositionObject(xhr.response.location.lat,
xhr.response.location.lng,
xhr.response.accuracy);
this.notifyListener("update", [newLocation]);
gCachedRequest = new CachedRequest(newLocation, data.cellTowers, data.wifiAccessPoints);
}).bind(this);
var requestData = JSON.stringify(data);
LOG("sending " + requestData);
xhr.send(requestData);
},
notifyListener: function(listenerFunc, args) {
args = args || [];
try {
this.listener[listenerFunc].apply(this.listener, args);
} catch(e) {
Cu.reportError(e);
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiGeoPositionProvider]);

View file

@ -0,0 +1,3 @@
component {77DA64D3-7458-4920-9491-86CC9914F904} NetworkGeolocationProvider.js
contract @mozilla.org/geolocation/provider;1 {77DA64D3-7458-4920-9491-86CC9914F904}
contract @mozilla.org/geolocation/mls-provider;1 {77DA64D3-7458-4920-9491-86CC9914F904}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_osfileconstants_h__
#define mozilla_osfileconstants_h__
#include "nsIOSFileConstantsService.h"
#include "mozilla/Attributes.h"
namespace mozilla {
/**
* Perform initialization of this module.
*
* This function _must_ be called:
* - from the main thread;
* - before any Chrome Worker is created.
*
* The function is idempotent.
*/
nsresult InitOSFileConstants();
/**
* Perform cleanup of this module.
*
* This function _must_ be called:
* - from the main thread;
* - after all Chrome Workers are dead.
*
* The function is idempotent.
*/
void CleanupOSFileConstants();
/**
* Define OS-specific constants.
*
* This function creates or uses JS object |OS.Constants| to store
* all its constants.
*/
bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global);
/**
* XPConnect initializer, for use in the main thread.
*/
class OSFileConstantsService final : public nsIOSFileConstantsService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOSFILECONSTANTSSERVICE
OSFileConstantsService();
private:
~OSFileConstantsService();
};
} // namespace mozilla
#endif // mozilla_osfileconstants_h__

View file

@ -0,0 +1,2 @@
component {e8530001-ba5b-46ab-a306-7fbeb692d0fe} SystemUpdateManager.js
contract @mozilla.org/system-update-manager;1 {e8530001-ba5b-46ab-a306-7fbeb692d0fe}

View file

@ -0,0 +1,253 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
var debug = Services.prefs.getBoolPref("dom.system_update.debug")
? (aMsg) => dump("-*- SystemUpdateManager.js : " + aMsg + "\n")
: (aMsg) => {};
const SYSTEMUPDATEPROVIDER_CID = Components.ID("{11fbea3d-fd94-459a-b8fb-557fe19e473a}");
const SYSTEMUPDATEMANAGER_CID = Components.ID("{e8530001-ba5b-46ab-a306-7fbeb692d0fe}");
const SYSTEMUPDATEMANAGER_CONTRACTID = "@mozilla.org/system-update-manager;1";
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
function SystemUpdateProvider(win, provider) {
this.initDOMRequestHelper(win, [
{name: "SystemUpdate:OnUpdateAvailable", weakRef: true},
{name: "SystemUpdate:OnProgress", weakRef: true},
{name: "SystemUpdate:OnUpdateReady", weakRef: true},
{name: "SystemUpdate:OnError", weakRef: true},
]);
this._provider = Cu.cloneInto(provider, win);
}
SystemUpdateProvider.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: SYSTEMUPDATEPROVIDER_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIObserver]),
receiveMessage: function(aMsg) {
if (!aMsg || !aMsg.json) {
return;
}
let json = aMsg.json;
if (json.uuid !== this._provider.uuid) {
return;
}
debug("receive msg: " + aMsg.name);
switch (aMsg.name) {
case "SystemUpdate:OnUpdateAvailable": {
let detail = {
detail: {
packageInfo: json.packageInfo
}
};
let event = new this._window.CustomEvent("updateavailable",
Cu.cloneInto(detail, this._window));
this.__DOM_IMPL__.dispatchEvent(event);
break;
}
case "SystemUpdate:OnProgress": {
let event = new this._window.ProgressEvent("progress", {lengthComputable: true,
loaded: json.loaded,
total: json.total});
this.__DOM_IMPL__.dispatchEvent(event);
break;
}
case "SystemUpdate:OnUpdateReady": {
let event = new this._window.Event("updateready");
this.__DOM_IMPL__.dispatchEvent(event);
break;
}
case "SystemUpdate:OnError": {
let event = new this._window.ErrorEvent("error", {message: json.message});
this.__DOM_IMPL__.dispatchEvent(event);
break;
}
}
},
destroy: function() {
this.destroyDOMRequestHelper();
},
get name() {
return this._provider.name;
},
get uuid() {
return this._provider.uuid;
},
get onupdateavailable() {
return this.__DOM_IMPL__.getEventHandler("onupdateavailable");
},
set onupdateavailable(aHandler) {
this.__DOM_IMPL__.setEventHandler("onupdateavailable", aHandler);
},
get onprogress() {
return this.__DOM_IMPL__.getEventHandler("onprogress");
},
set onprogress(aHandler) {
this.__DOM_IMPL__.setEventHandler("onprogress", aHandler);
},
get onupdateready() {
return this.__DOM_IMPL__.getEventHandler("onupdateready");
},
set onupdateready(aHandler) {
this.__DOM_IMPL__.setEventHandler("onupdateready", aHandler);
},
get onerror() {
return this.__DOM_IMPL__.getEventHandler("onerror");
},
set onerror(aHandler) {
this.__DOM_IMPL__.setEventHandler("onerror", aHandler);
},
checkForUpdate: function() {
let self = this;
cpmm.sendAsyncMessage("SystemUpdate:CheckForUpdate", {
uuid: self._provider.uuid
});
},
startDownload: function() {
let self = this;
cpmm.sendAsyncMessage("SystemUpdate:StartDownload", {
uuid: self._provider.uuid
});
},
stopDownload: function() {
let self = this;
cpmm.sendAsyncMessage("SystemUpdate:StopDownload", {
uuid: self._provider.uuid
});
},
applyUpdate: function() {
let self = this;
cpmm.sendAsyncMessage("SystemUpdate:ApplyUpdate", {
uuid: self._provider.uuid
});
},
setParameter: function(aName, aValue) {
let self = this;
return cpmm.sendSyncMessage("SystemUpdate:SetParameter", {
uuid: self._provider.uuid,
name: aName,
value: aValue
})[0];
},
getParameter: function(aName) {
let self = this;
return cpmm.sendSyncMessage("SystemUpdate:GetParameter", {
uuid: self._provider.uuid,
name: aName
})[0];
},
};
function SystemUpdateManager() {}
SystemUpdateManager.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: SYSTEMUPDATEMANAGER_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIObserver,
Ci.nsIDOMGlobalPropertyInitializer]),
receiveMessage: function(aMsg) {
if (!aMsg || !aMsg.json) {
return;
}
let json = aMsg.json;
let resolver = this.takePromiseResolver(json.requestId);
if (!resolver) {
return;
}
debug("receive msg: " + aMsg.name);
switch (aMsg.name) {
case "SystemUpdate:GetProviders:Result:OK": {
resolver.resolve(Cu.cloneInto(json.providers, this._window));
break;
}
case "SystemUpdate:SetActiveProvider:Result:OK":
case "SystemUpdate:GetActiveProvider:Result:OK": {
let updateProvider = new SystemUpdateProvider(this._window, json.provider);
resolver.resolve(this._window.SystemUpdateProvider._create(this._window,
updateProvider));
break;
}
case "SystemUpdate:GetProviders:Result:Error":
case "SystemUpdate:GetActiveProvider:Result:Error":
case "SystemUpdate:SetActiveProvider:Result:Error": {
resolver.reject(json.error);
break;
}
}
},
init: function(aWindow) {
this.initDOMRequestHelper(aWindow, [
{name: "SystemUpdate:GetProviders:Result:OK", weakRef: true},
{name: "SystemUpdate:GetProviders:Result:Error", weakRef: true},
{name: "SystemUpdate:GetActiveProvider:Result:OK", weakRef: true},
{name: "SystemUpdate:GetActiveProvider:Result:Error", weakRef: true},
{name: "SystemUpdate:SetActiveProvider:Result:OK", weakRef: true},
{name: "SystemUpdate:SetActiveProvider:Result:Error", weakRef: true},
]);
},
uninit: function() {
let self = this;
this.forEachPromiseResolver(function(aKey) {
self.takePromiseResolver(aKey).reject("SystemUpdateManager got destroyed");
});
},
getProviders: function() {
return this.createPromiseWithId(function(aResolverId) {
cpmm.sendAsyncMessage("SystemUpdate:GetProviders", {
requestId: aResolverId,
});
});
},
getActiveProvider: function() {
return this.createPromiseWithId(function(aResolverId) {
cpmm.sendAsyncMessage("SystemUpdate:GetActiveProvider", {
requestId: aResolverId,
});
});
},
setActiveProvider: function(aUuid) {
return this.createPromiseWithId(function(aResolverId) {
cpmm.sendAsyncMessage("SystemUpdate:SetActiveProvider", {
requestId: aResolverId,
uuid: aUuid
});
});
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SystemUpdateManager]);

View file

@ -0,0 +1,382 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
this.EXPORTED_SYMBOLS = ["SystemUpdateService"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const CATEGORY_SYSTEM_UPDATE_PROVIDER = "system-update-provider";
const PROVIDER_ACTIVITY_IDLE = 0;
const PROVIDER_ACTIVITY_CHECKING = 1;
const PROVIDER_ACTIVITY_DOWNLOADING = 1 << 1;
const PROVIDER_ACTIVITY_APPLYING = 1 << 2;
var debug = Services.prefs.getBoolPref("dom.system_update.debug")
? (aMsg) => dump("-*- SystemUpdateService.jsm : " + aMsg + "\n")
: (aMsg) => {};
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
function ActiveProvider(aProvider) {
this.id = aProvider.id;
this._instance = Components.classesByID[aProvider.id].getService(Ci.nsISystemUpdateProvider);
this._instance.setListener(this);
}
ActiveProvider.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemUpdateListener]),
_activity: PROVIDER_ACTIVITY_IDLE,
destroy: function() {
if (this._instance) {
this._instance.unsetListener();
this._instance = null;
}
this.id = null;
},
checkForUpdate: function() {
this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_CHECKING,
this._instance.checkForUpdate);
},
startDownload: function() {
this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING,
this._instance.startDownload);
},
stopDownload: function() {
this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING,
this._instance.stopDownload);
},
applyUpdate: function() {
this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_APPLYING,
this._instance.applyUpdate);
},
setParameter: function(aName, aValue) {
return this._instance.setParameter(aName, aValue);
},
getParameter: function(aName) {
return this._instance.getParameter(aName);
},
// nsISystemUpdateListener
onUpdateAvailable: function(aType, aVersion, aDescription, aBuildDate, aSize) {
this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_CHECKING, function() {
ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateAvailable", {
uuid: this.id,
packageInfo: {
type: aType,
version: aVersion,
description: aDescription,
buildDate: aBuildDate,
size: aSize,
}
});
this._unsetActivity(PROVIDER_ACTIVITY_CHECKING);
}.bind(this));
},
onProgress: function(aLoaded, aTotal) {
this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() {
ppmm.broadcastAsyncMessage("SystemUpdate:OnProgress", {
uuid: this.id,
loaded: aLoaded,
total: aTotal,
});
}.bind(this));
},
onUpdateReady: function() {
this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() {
ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateReady", {
uuid: this.id,
});
this._unsetActivity(PROVIDER_ACTIVITY_DOWNLOADING);
}.bind(this));
},
onError: function(aErrMsg) {
if (!SystemUpdateService._isActiveProviderId(this.id)) {
return;
}
ppmm.broadcastAsyncMessage("SystemUpdate:OnError", {
uuid: this.id,
message: aErrMsg,
});
this._activity = PROVIDER_ACTIVITY_IDLE;
},
isIdle: function() {
return this._activity === PROVIDER_ACTIVITY_IDLE;
},
_isInActivity: function(aActivity) {
return (this._activity & aActivity) !== PROVIDER_ACTIVITY_IDLE;
},
_setActivity: function(aActivity) {
this._activity |= aActivity;
},
_unsetActivity: function(aActivity) {
this._activity &= ~aActivity;
},
_execFuncIfNotInActivity: function(aActivity, aFunc) {
if (!this._isInActivity(aActivity)) {
this._setActivity(aActivity);
aFunc();
}
},
_execFuncIfActiveAndInAction: function(aActivity, aFunc) {
if (!SystemUpdateService._isActiveProviderId(this.id)) {
return;
}
if (this._isInActivity(aActivity)) {
aFunc();
}
},
};
this.SystemUpdateService = {
_providers: [],
_activeProvider: null,
_updateActiveProvider: function(aProvider) {
if (this._activeProvider) {
this._activeProvider.destroy();
}
this._activeProvider = new ActiveProvider(aProvider);
},
_isActiveProviderId: function(aId) {
return (this._activeProvider && this._activeProvider.id === aId);
},
init: function() {
debug("init");
let messages = ["SystemUpdate:GetProviders",
"SystemUpdate:GetActiveProvider",
"SystemUpdate:SetActiveProvider",
"SystemUpdate:CheckForUpdate",
"SystemUpdate:StartDownload",
"SystemUpdate:StopDownload",
"SystemUpdate:ApplyUpdate",
"SystemUpdate:SetParameter",
"SystemUpdate:GetParameter"];
messages.forEach((function(aMsgName) {
ppmm.addMessageListener(aMsgName, this);
}).bind(this));
// load available provider list
let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
let entries = catMan.enumerateCategory(CATEGORY_SYSTEM_UPDATE_PROVIDER);
while (entries.hasMoreElements()) {
let name = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
let [contractId, id] = catMan.getCategoryEntry(CATEGORY_SYSTEM_UPDATE_PROVIDER, name).split(",");
this._providers.push({
id: id,
name: name,
contractId: contractId
});
}
debug("available providers: " + JSON.stringify(this._providers));
// setup default active provider
let defaultActive;
try {
defaultActive = Services.prefs.getCharPref("dom.system_update.active");
} catch (e) {}
if (defaultActive) {
let defaultProvider = this._providers.find(function(aProvider) {
return aProvider.contractId === defaultActive;
});
if (defaultProvider) {
this._updateActiveProvider(defaultProvider);
}
}
},
addProvider: function(aClassId, aContractId, aName) {
debug("addProvider");
//did not allow null or empty string to add.
if(!aClassId || !aContractId || !aName) {
return;
}
let existedProvider = this._providers.find(function(provider) {
return provider.id === aClassId;
});
//skip if adding the existed provider.
if (existedProvider) {
debug("existing providers: " + JSON.stringify(existedProvider));
return;
}
//dynamically add the provider info to list.
this._providers.push({
id: aClassId,
name: aName,
contractId: aContractId
});
debug("available providers: " + JSON.stringify(this._providers));
},
getProviders: function(aData, aMm) {
debug("getProviders");
aData.providers = [];
for (let provider of this._providers) {
aData.providers.push({
name: provider.name,
uuid: provider.id
});
}
aMm.sendAsyncMessage("SystemUpdate:GetProviders:Result:OK", aData);
},
getActiveProvider: function(aData, aMm) {
debug("getActiveProvider");
let self = this;
let providerInfo = this._providers.find(function(provider) {
return self._isActiveProviderId(provider.id);
});
if (!providerInfo) {
aData.error = "NotFoundError";
aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:Error", aData);
return;
}
aData.provider = {
name: providerInfo.name,
uuid: providerInfo.id
};
aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:OK", aData);
},
setActiveProvider: function(aData, aMm) {
debug("setActiveProvider");
let self = this;
let selectedProvider = this._providers.find(function(provider) {
return provider.id === aData.uuid;
});
if (!selectedProvider) {
aData.error = "DataError";
aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData);
return;
}
if (!this._isActiveProviderId(selectedProvider.id)) {
// not allow changing active provider while there is an ongoing update activity
if (this.activeProvider && !this._activeProvider.isIdle()) {
aData.error = "DataError";
aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData);
return;
}
this._updateActiveProvider(selectedProvider);
Services.prefs.setCharPref("dom.system_update.active", selectedProvider.contractId);
}
aData.provider = {
name: selectedProvider.name,
uuid: selectedProvider.id
};
aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:OK", aData);
},
receiveMessage: function(aMessage) {
if (!aMessage.target.assertPermission("system-update")) {
debug("receive message " + aMessage.name +
" from a content process with no 'system-update' privileges.");
return null;
}
let msg = aMessage.data || {};
let mm = aMessage.target;
switch (aMessage.name) {
case "SystemUpdate:GetProviders": {
this.getProviders(msg, mm);
break;
}
case "SystemUpdate:GetActiveProvider": {
this.getActiveProvider(msg, mm);
break;
}
case "SystemUpdate:SetActiveProvider": {
this.setActiveProvider(msg, mm);
break;
}
case "SystemUpdate:CheckForUpdate": {
if (this._isActiveProviderId(msg.uuid)) {
this._activeProvider.checkForUpdate();
}
break;
}
case "SystemUpdate:StartDownload": {
if (this._isActiveProviderId(msg.uuid)) {
this._activeProvider.startDownload();
}
break;
}
case "SystemUpdate:StopDownload": {
if (this._isActiveProviderId(msg.uuid)) {
this._activeProvider.stopDownload();
}
break;
}
case "SystemUpdate:ApplyUpdate": {
if (this._isActiveProviderId(msg.uuid)) {
this._activeProvider.applyUpdate();
}
break;
}
case "SystemUpdate:SetParameter": {
if (this._isActiveProviderId(msg.uuid)) {
return this._activeProvider.setParameter(msg.name, msg.value);
}
break;
}
case "SystemUpdate:GetParameter": {
if (this._isActiveProviderId(msg.uuid)) {
return this._activeProvider.getParameter(msg.name);
}
break;
}
}
},
};
SystemUpdateService.init();

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsGeolocation.h"
#include "nsGeoPosition.h"
#include "AndroidLocationProvider.h"
#include "GeneratedJNIWrappers.h"
using namespace mozilla;
extern nsIGeolocationUpdate *gLocationCallback;
NS_IMPL_ISUPPORTS(AndroidLocationProvider, nsIGeolocationProvider)
AndroidLocationProvider::AndroidLocationProvider()
{
}
AndroidLocationProvider::~AndroidLocationProvider()
{
NS_IF_RELEASE(gLocationCallback);
}
NS_IMETHODIMP
AndroidLocationProvider::Startup()
{
java::GeckoAppShell::EnableLocation(true);
return NS_OK;
}
NS_IMETHODIMP
AndroidLocationProvider::Watch(nsIGeolocationUpdate* aCallback)
{
NS_IF_RELEASE(gLocationCallback);
gLocationCallback = aCallback;
NS_IF_ADDREF(gLocationCallback);
return NS_OK;
}
NS_IMETHODIMP
AndroidLocationProvider::Shutdown()
{
java::GeckoAppShell::EnableLocation(false);
return NS_OK;
}
NS_IMETHODIMP
AndroidLocationProvider::SetHighAccuracy(bool enable)
{
java::GeckoAppShell::EnableLocationHighAccuracy(enable);
return NS_OK;
}

View file

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef AndroidLocationProvider_h
#define AndroidLocationProvider_h
#include "nsIGeolocationProvider.h"
class AndroidLocationProvider final : public nsIGeolocationProvider
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGEOLOCATIONPROVIDER
AndroidLocationProvider();
private:
~AndroidLocationProvider();
};
#endif /* AndroidLocationProvider_h */

View file

@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
SOURCES += [
'AndroidLocationProvider.cpp',
'nsHapticFeedback.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/geolocation',
]

View file

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/dom/ContentChild.h"
#include "nsHapticFeedback.h"
#include "GeneratedJNIWrappers.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS(nsHapticFeedback, nsIHapticFeedback)
NS_IMETHODIMP
nsHapticFeedback::PerformSimpleAction(int32_t aType)
{
java::GeckoAppShell::PerformHapticFeedback(aType == LongPress);
return NS_OK;
}

View file

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsIHapticFeedback.h"
class nsHapticFeedback final : public nsIHapticFeedback
{
private:
~nsHapticFeedback() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHAPTICFEEDBACK
};

View file

@ -0,0 +1,181 @@
/* 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/. */
#include "nsIDocument.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
#include "nsPIDOMWindow.h"
#include "nsIDocShell.h"
#include "nsIPermissionManager.h"
#include "nsIInterfaceRequestorUtils.h"
#include "AudioChannelManager.h"
#include "mozilla/dom/AudioChannelManagerBinding.h"
#include "mozilla/dom/nsBrowserElement.h"
#include "mozilla/Services.h"
namespace mozilla {
namespace dom {
namespace system {
NS_IMPL_QUERY_INTERFACE_INHERITED(AudioChannelManager, DOMEventTargetHelper,
nsIDOMEventListener)
NS_IMPL_ADDREF_INHERITED(AudioChannelManager, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(AudioChannelManager, DOMEventTargetHelper)
AudioChannelManager::AudioChannelManager()
: mVolumeChannel(-1)
{
hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
}
AudioChannelManager::~AudioChannelManager()
{
hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
/* useCapture = */ true);
}
void
AudioChannelManager::Init(nsPIDOMWindowInner* aWindow)
{
BindToOwner(aWindow);
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
}
JSObject*
AudioChannelManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return AudioChannelManagerBinding::Wrap(aCx, this, aGivenProto);
}
void
AudioChannelManager::Notify(const hal::SwitchEvent& aEvent)
{
mState = Some(aEvent.status());
DispatchTrustedEvent(NS_LITERAL_STRING("headphoneschange"));
}
bool
AudioChannelManager::SetVolumeControlChannel(const nsAString& aChannel)
{
if (aChannel.EqualsASCII("publicnotification")) {
return false;
}
AudioChannel newChannel = AudioChannelService::GetAudioChannel(aChannel);
// Only normal channel doesn't need permission.
if (newChannel != AudioChannel::Normal) {
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return false;
}
uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
permissionManager->TestPermissionFromWindow(GetOwner(),
nsCString(NS_LITERAL_CSTRING("audio-channel-") +
NS_ConvertUTF16toUTF8(aChannel)).get(), &perm);
if (perm != nsIPermissionManager::ALLOW_ACTION) {
return false;
}
}
if (mVolumeChannel == (int32_t)newChannel) {
return true;
}
mVolumeChannel = (int32_t)newChannel;
NotifyVolumeControlChannelChanged();
return true;
}
bool
AudioChannelManager::GetVolumeControlChannel(nsAString & aChannel)
{
if (mVolumeChannel >= 0) {
AudioChannelService::GetAudioChannelString(
static_cast<AudioChannel>(mVolumeChannel),
aChannel);
} else {
aChannel.AssignASCII("");
}
return true;
}
void
AudioChannelManager::NotifyVolumeControlChannelChanged()
{
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
NS_ENSURE_TRUE_VOID(docshell);
bool isActive = false;
docshell->GetIsActive(&isActive);
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (!service) {
return;
}
if (isActive) {
service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
} else {
service->SetDefaultVolumeControlChannel(-1, isActive);
}
}
NS_IMETHODIMP
AudioChannelManager::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (type.EqualsLiteral("visibilitychange")) {
NotifyVolumeControlChannelChanged();
}
return NS_OK;
}
void
AudioChannelManager::GetAllowedAudioChannels(
nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
ErrorResult& aRv)
{
MOZ_ASSERT(aAudioChannels.IsEmpty());
// Only main process is supported.
if (XRE_GetProcessType() != GeckoProcessType_Default) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsBrowserElement::GenerateAllowedAudioChannels(window, nullptr, nullptr,
aAudioChannels, aRv);
NS_WARNING_ASSERTION(!aRv.Failed(), "GenerateAllowedAudioChannels failed");
}
} // namespace system
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,87 @@
/* 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/. */
#ifndef mozilla_dom_system_AudioChannelManager_h
#define mozilla_dom_system_AudioChannelManager_h
#include "mozilla/dom/BrowserElementAudioChannel.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/Hal.h"
#include "mozilla/HalTypes.h"
#include "mozilla/Maybe.h"
#include "AudioChannelService.h"
namespace mozilla {
namespace hal {
class SwitchEvent;
typedef Observer<SwitchEvent> SwitchObserver;
} // namespace hal
namespace dom {
namespace system {
class AudioChannelManager final
: public DOMEventTargetHelper
, public hal::SwitchObserver
, public nsIDOMEventListener
{
public:
AudioChannelManager();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMEVENTLISTENER
void Notify(const hal::SwitchEvent& aEvent);
void Init(nsPIDOMWindowInner* aWindow);
/**
* WebIDL Interface
*/
nsPIDOMWindowInner* GetParentObject() const
{
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
bool Headphones()
{
// Bug 929139 - Remove the assert check for SWITCH_STATE_UNKNOWN.
// If any devices (ex: emulator) didn't have the corresponding sys node for
// headset switch state then GonkSwitch will report the unknown state.
// So it is possible to get unknown state here.
if (mState.isNothing()) {
mState = Some(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
}
return mState.value() != hal::SWITCH_STATE_OFF &&
mState.value() != hal::SWITCH_STATE_UNKNOWN;
}
bool SetVolumeControlChannel(const nsAString& aChannel);
bool GetVolumeControlChannel(nsAString& aChannel);
IMPL_EVENT_HANDLER(headphoneschange)
void GetAllowedAudioChannels(
nsTArray<RefPtr<mozilla::dom::BrowserElementAudioChannel>>& aAudioChannels,
mozilla::ErrorResult& aRv);
protected:
virtual ~AudioChannelManager();
private:
void NotifyVolumeControlChannelChanged();
Maybe<hal::SwitchState> mState;
int32_t mVolumeChannel;
};
} // namespace system
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_system_AudioChannelManager_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,180 @@
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef mozilla_dom_system_b2g_audiomanager_h__
#define mozilla_dom_system_b2g_audiomanager_h__
#include "mozilla/HalTypes.h"
#include "mozilla/Observer.h"
#include "mozilla/UniquePtr.h"
#include "nsAutoPtr.h"
#include "nsDataHashtable.h"
#include "nsIAudioManager.h"
#include "nsIObserver.h"
#include "android_audio/AudioSystem.h"
// {b2b51423-502d-4d77-89b3-7786b562b084}
#define NS_AUDIOMANAGER_CID {0x94f6fd70, 0x7615, 0x4af9, \
{0x89, 0x10, 0xf9, 0x3c, 0x55, 0xe6, 0x62, 0xec}}
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
class nsISettingsServiceLock;
namespace mozilla {
namespace hal {
class SwitchEvent;
typedef Observer<SwitchEvent> SwitchObserver;
} // namespace hal
namespace dom {
namespace gonk {
class VolumeInitCallback;
class AudioManager final : public nsIAudioManager
, public nsIObserver
{
public:
static already_AddRefed<AudioManager> GetInstance();
NS_DECL_ISUPPORTS
NS_DECL_NSIAUDIOMANAGER
NS_DECL_NSIOBSERVER
// Validate whether the volume index is within the range
nsresult ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const;
// Called when android AudioFlinger in mediaserver is died
void HandleAudioFlingerDied();
void HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent);
class VolumeStreamState {
public:
explicit VolumeStreamState(AudioManager& aManager, int32_t aStreamType);
int32_t GetStreamType()
{
return mStreamType;
}
bool IsDevicesChanged(bool aFromCache = true);
void ClearDevicesChanged();
uint32_t GetLastDevices()
{
return mLastDevices;
}
bool IsVolumeIndexesChanged();
void ClearVolumeIndexesChanged();
void InitStreamVolume();
uint32_t GetMaxIndex();
uint32_t GetDefaultIndex();
uint32_t GetVolumeIndex();
uint32_t GetVolumeIndex(uint32_t aDevice);
void ClearCurrentVolumeUpdated();
// Set volume index to all active devices.
// Active devices are chosen by android AudioPolicyManager.
nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex);
// Set volume index to all alias streams for device. Alias streams have same volume.
nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice);
nsresult SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice);
nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true);
// Restore volume index to all devices. Called when AudioFlinger is restarted.
void RestoreVolumeIndexToAllDevices();
private:
AudioManager& mManager;
const int32_t mStreamType;
uint32_t mLastDevices;
bool mIsDevicesChanged;
bool mIsVolumeIndexesChanged;
nsDataHashtable<nsUint32HashKey, uint32_t> mVolumeIndexes;
};
protected:
int32_t mPhoneState;
bool mIsVolumeInited;
// A bitwise variable for volume update of audio output devices,
// clear it after store the value into database.
uint32_t mAudioOutDevicesUpdated;
// Connected devices that are controlled by setDeviceConnectionState()
nsDataHashtable<nsUint32HashKey, nsCString> mConnectedDevices;
nsDataHashtable<nsUint32HashKey, uint32_t> mAudioDeviceTableIdMaps;
bool mSwitchDone;
#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
bool mBluetoothA2dpEnabled;
#endif
#ifdef MOZ_B2G_BT
bool mA2dpSwitchDone;
#endif
nsTArray<UniquePtr<VolumeStreamState> > mStreamStates;
uint32_t mLastChannelVolume[AUDIO_STREAM_CNT];
bool IsFmOutConnected();
nsresult SetStreamVolumeForDevice(int32_t aStream,
uint32_t aIndex,
uint32_t aDevice);
nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t* aIndex);
void UpdateCachedActiveDevicesForStreams();
uint32_t GetDevicesForStream(int32_t aStream, bool aFromCache = true);
uint32_t GetDeviceForStream(int32_t aStream);
// Choose one device as representative of active devices.
static uint32_t SelectDeviceFromDevices(uint32_t aOutDevices);
private:
nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
void HandleBluetoothStatusChanged(nsISupports* aSubject,
const char* aTopic,
const nsCString aAddress);
void HandleAudioChannelProcessChanged();
// Append the audio output device to the volume setting string.
nsAutoCString AppendDeviceToVolumeSetting(const char* aName,
uint32_t aDevice);
// We store the volume setting in the database, these are related functions.
void InitVolumeFromDatabase();
void MaybeUpdateVolumeSettingToDatabase(bool aForce = false);
// Promise functions.
void InitDeviceVolumeSucceeded();
void InitDeviceVolumeFailed(const char* aError);
void AudioOutDeviceUpdated(uint32_t aDevice);
void UpdateHeadsetConnectionState(hal::SwitchState aState);
void UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName);
void SetAllDeviceConnectionStates();
AudioManager();
~AudioManager();
friend class VolumeInitCallback;
friend class VolumeStreamState;
friend class GonkAudioPortCallback;
};
} /* namespace gonk */
} /* namespace dom */
} /* namespace mozilla */
#endif // mozilla_dom_system_b2g_audiomanager_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
/* 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/. */
#ifndef mozilla_system_automounter_h__
#define mozilla_system_automounter_h__
#include <stdint.h>
class nsCString;
namespace mozilla {
namespace system {
// AutoMounter modes
#define AUTOMOUNTER_DISABLE 0
#define AUTOMOUNTER_ENABLE_UMS 1
#define AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED 2
#define AUTOMOUNTER_ENABLE_MTP 3
// Automounter statuses
#define AUTOMOUNTER_STATUS_DISABLED 0
#define AUTOMOUNTER_STATUS_ENABLED 1
#define AUTOMOUNTER_STATUS_FILES_OPEN 2
/**
* Initialize the automounter. This causes some of the phone's
* directories to show up on the host when the phone is plugged
* into the host via USB.
*
* When the AutoMounter starts, it will poll the current state
* of affairs (usb cable plugged in, automounter enabled, etc)
* and try to make the state of the volumes match.
*/
void
InitAutoMounter();
/**
* Sets the enabled state of the automounter.
*
* This will in turn cause the automounter to re-evaluate
* whether it should mount/unmount/share/unshare volumes.
*/
void
SetAutoMounterMode(int32_t aMode);
/**
* Reports the status of the automounter.
*/
int32_t
GetAutoMounterStatus();
/**
* Sets the sharing mode of an individual volume.
*
* If a volume is enabled for sharing, and the autmounter
* is in a state to share, then the volume will be shared
* with the PC.
*/
void
SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing);
/**
* Formats the volume with specified volume name.
*
* If the volume is ready to format, automounter
* will unmount it, format it and then mount it again.
*/
void
AutoMounterFormatVolume(const nsCString& aVolumeName);
/**
* Mounts the volume with specified volume name.
*
* If the volume is already unmounted, automounter
* will mount it. Otherwise automounter will skip this.
*/
void
AutoMounterMountVolume(const nsCString& aVolumeName);
/**
* Unmounts the volume with specified volume name.
*
* If the volume is already mounted, automounter
* will unmount it. Otherwise automounter will skip this.
*/
void
AutoMounterUnmountVolume(const nsCString& aVolumeName);
/**
* Shuts down the automounter.
*
* This leaves the volumes in whatever state they're in.
*/
void
ShutdownAutoMounter();
} // system
} // mozilla
#endif // mozilla_system_automounter_h__

View file

@ -0,0 +1,284 @@
/* 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/. */
#include "AutoMounter.h"
#include "AutoMounterSetting.h"
#include "base/message_loop.h"
#include "jsapi.h"
#include "mozilla/Services.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsJSUtils.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#undef LOG
#undef ERR
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounterSetting" , ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounterSetting" , ## args)
#define UMS_MODE "ums.mode"
#define UMS_STATUS "ums.status"
#define UMS_VOLUME_ENABLED_PREFIX "ums.volume."
#define UMS_VOLUME_ENABLED_SUFFIX ".enabled"
#define MOZSETTINGS_CHANGED "mozsettings-changed"
using namespace mozilla::dom;
namespace mozilla {
namespace system {
class SettingsServiceCallback final : public nsISettingsServiceCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
SettingsServiceCallback() {}
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
{
if (aResult.isInt32()) {
int32_t mode = aResult.toInt32();
SetAutoMounterMode(mode);
}
return NS_OK;
}
NS_IMETHOD HandleError(const nsAString& aName)
{
ERR("SettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
return NS_OK;
}
protected:
~SettingsServiceCallback() {}
};
NS_IMPL_ISUPPORTS(SettingsServiceCallback, nsISettingsServiceCallback)
class CheckVolumeSettingsCallback final : public nsISettingsServiceCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
CheckVolumeSettingsCallback(const nsACString& aVolumeName)
: mVolumeName(aVolumeName) {}
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
{
if (aResult.isBoolean()) {
bool isSharingEnabled = aResult.toBoolean();
SetAutoMounterSharingMode(mVolumeName, isSharingEnabled);
}
return NS_OK;
}
NS_IMETHOD HandleError(const nsAString& aName)
{
ERR("CheckVolumeSettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
return NS_OK;
}
protected:
~CheckVolumeSettingsCallback() {}
private:
nsCString mVolumeName;
};
NS_IMPL_ISUPPORTS(CheckVolumeSettingsCallback, nsISettingsServiceCallback)
AutoMounterSetting::AutoMounterSetting()
: mStatus(AUTOMOUNTER_STATUS_DISABLED)
{
MOZ_ASSERT(NS_IsMainThread());
// Setup an observer to watch changes to the setting
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService) {
ERR("GetObserverService failed");
return;
}
nsresult rv;
rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
if (NS_FAILED(rv)) {
ERR("AddObserver failed");
return;
}
// Force ums.mode to be 0 initially. We do this because settings are persisted.
// We don't want UMS to be enabled until such time as the phone is unlocked,
// and gaia/apps/system/js/storage.js takes care of detecting when the phone
// becomes unlocked and changes ums.mode appropriately.
nsCOMPtr<nsISettingsService> settingsService =
do_GetService("@mozilla.org/settingsService;1");
if (!settingsService) {
ERR("Failed to get settingsLock service!");
return;
}
nsCOMPtr<nsISettingsServiceLock> lock;
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
nsCOMPtr<nsISettingsServiceCallback> callback = new SettingsServiceCallback();
JS::Rooted<JS::Value> value(RootingCx());
value.setInt32(AUTOMOUNTER_DISABLE);
lock->Set(UMS_MODE, value, callback, nullptr);
value.setInt32(mStatus);
lock->Set(UMS_STATUS, value, nullptr, nullptr);
}
AutoMounterSetting::~AutoMounterSetting()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
}
}
NS_IMPL_ISUPPORTS(AutoMounterSetting, nsIObserver)
const char *
AutoMounterSetting::StatusStr(int32_t aStatus)
{
switch (aStatus) {
case AUTOMOUNTER_STATUS_DISABLED: return "Disabled";
case AUTOMOUNTER_STATUS_ENABLED: return "Enabled";
case AUTOMOUNTER_STATUS_FILES_OPEN: return "FilesOpen";
}
return "??? Unknown ???";
}
class CheckVolumeSettingsRunnable : public Runnable
{
public:
CheckVolumeSettingsRunnable(const nsACString& aVolumeName)
: mVolumeName(aVolumeName) {}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsISettingsService> settingsService =
do_GetService("@mozilla.org/settingsService;1");
NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
nsCOMPtr<nsISettingsServiceLock> lock;
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
nsCOMPtr<nsISettingsServiceCallback> callback =
new CheckVolumeSettingsCallback(mVolumeName);
nsPrintfCString setting(UMS_VOLUME_ENABLED_PREFIX "%s" UMS_VOLUME_ENABLED_SUFFIX,
mVolumeName.get());
lock->Get(setting.get(), callback);
return NS_OK;
}
private:
nsCString mVolumeName;
};
//static
void
AutoMounterSetting::CheckVolumeSettings(const nsACString& aVolumeName)
{
NS_DispatchToMainThread(new CheckVolumeSettingsRunnable(aVolumeName));
}
class SetStatusRunnable : public Runnable
{
public:
SetStatusRunnable(int32_t aStatus) : mStatus(aStatus) {}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsISettingsService> settingsService =
do_GetService("@mozilla.org/settingsService;1");
NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
nsCOMPtr<nsISettingsServiceLock> lock;
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
// lock may be null if this gets called during shutdown.
if (lock) {
JS::Rooted<JS::Value> value(RootingCx(),
JS::Int32Value(mStatus));
lock->Set(UMS_STATUS, value, nullptr, nullptr);
}
return NS_OK;
}
private:
int32_t mStatus;
};
//static
void
AutoMounterSetting::SetStatus(int32_t aStatus)
{
if (aStatus != mStatus) {
LOG("Changing status from '%s' to '%s'",
StatusStr(mStatus), StatusStr(aStatus));
mStatus = aStatus;
NS_DispatchToMainThread(new SetStatusRunnable(aStatus));
}
}
NS_IMETHODIMP
AutoMounterSetting::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
return NS_OK;
}
// Note that this function gets called for any and all settings changes,
// so we need to carefully check if we have the one we're interested in.
//
// The string that we're interested in will be a JSON string that looks like:
// {"key":"ums.autoMount","value":true}
RootedDictionary<SettingChangeNotification> setting(RootingCx());
if (!WrappedJSToDictionary(aSubject, setting)) {
return NS_OK;
}
// Check for ums.mode changes
if (setting.mKey.EqualsASCII(UMS_MODE)) {
if (!setting.mValue.isInt32()) {
return NS_OK;
}
int32_t mode = setting.mValue.toInt32();
SetAutoMounterMode(mode);
return NS_OK;
}
// Check for ums.volume.NAME.enabled
if (StringBeginsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_PREFIX)) &&
StringEndsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_SUFFIX))) {
if (!setting.mValue.isBoolean()) {
return NS_OK;
}
const size_t prefixLen = sizeof(UMS_VOLUME_ENABLED_PREFIX) - 1;
const size_t suffixLen = sizeof(UMS_VOLUME_ENABLED_SUFFIX) - 1;
nsDependentSubstring volumeName =
Substring(setting.mKey, prefixLen, setting.mKey.Length() - prefixLen - suffixLen);
bool isSharingEnabled = setting.mValue.toBoolean();
SetAutoMounterSharingMode(NS_LossyConvertUTF16toASCII(volumeName), isSharingEnabled);
return NS_OK;
}
return NS_OK;
}
} // namespace system
} // namespace mozilla

View file

@ -0,0 +1,38 @@
/* 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/. */
#ifndef mozilla_system_automountersetting_h__
#define mozilla_system_automountersetting_h__
#include "nsIObserver.h"
namespace mozilla {
namespace system {
class AutoMounterSetting : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
AutoMounterSetting();
static void CheckVolumeSettings(const nsACString& aVolumeName);
int32_t GetStatus() { return mStatus; }
void SetStatus(int32_t aStatus);
const char *StatusStr(int32_t aStatus);
protected:
virtual ~AutoMounterSetting();
private:
int32_t mStatus;
};
} // namespace system
} // namespace mozilla
#endif // mozilla_system_automountersetting_h__

View file

@ -0,0 +1,276 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const DATACALLINTERFACE_CONTRACTID = "@mozilla.org/datacall/interface;1";
const DATACALLINTERFACESERVICE_CONTRACTID =
"@mozilla.org/datacall/interfaceservice;1";
const DATACALLINTERFACE_CID =
Components.ID("{ff669306-4390-462a-989b-ba37fc42153f}");
const DATACALLINTERFACESERVICE_CID =
Components.ID("{e23e9337-592d-40b9-8cef-7bd47c28b72e}");
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
const TOPIC_PREF_CHANGED = "nsPref:changed";
const PREF_RIL_DEBUG_ENABLED = "ril.debugging.enabled";
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
let obj = {};
Cu.import("resource://gre/modules/ril_consts.js", obj);
return obj;
});
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
var DEBUG = RIL.DEBUG_RIL;
function updateDebugFlag() {
// Read debug setting from pref
let debugPref;
try {
debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED);
} catch (e) {
debugPref = false;
}
DEBUG = debugPref || RIL.DEBUG_RIL;
}
updateDebugFlag();
function DataCall(aAttributes) {
for (let key in aAttributes) {
if (key === "pdpType") {
// Convert pdp type into constant int value.
this[key] = RIL.RIL_DATACALL_PDP_TYPES.indexOf(aAttributes[key]);
continue;
}
this[key] = aAttributes[key];
}
}
DataCall.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCall]),
failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
suggestedRetryTime: -1,
cid: -1,
active: -1,
pdpType: -1,
ifname: null,
addreses: null,
dnses: null,
gateways: null,
pcscf: null,
mtu: -1
};
function DataCallInterfaceService() {
this._dataCallInterfaces = [];
let numClients = gRil.numRadioInterfaces;
for (let i = 0; i < numClients; i++) {
this._dataCallInterfaces.push(new DataCallInterface(i));
}
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
Services.prefs.addObserver(PREF_RIL_DEBUG_ENABLED, this, false);
}
DataCallInterfaceService.prototype = {
classID: DATACALLINTERFACESERVICE_CID,
classInfo: XPCOMUtils.generateCI({
classID: DATACALLINTERFACESERVICE_CID,
contractID: DATACALLINTERFACESERVICE_CONTRACTID,
classDescription: "Data Call Interface Service",
interfaces: [Ci.nsIDataCallInterfaceService,
Ci.nsIGonkDataCallInterfaceService]
}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterfaceService,
Ci.nsIGonkDataCallInterfaceService],
Ci.nsIObserver),
// An array of DataCallInterface instances.
_dataCallInterfaces: null,
debug: function(aMessage) {
dump("-*- DataCallInterfaceService: " + aMessage + "\n");
},
// nsIDataCallInterfaceService
getDataCallInterface: function(aClientId) {
let dataCallInterface = this._dataCallInterfaces[aClientId];
if (!dataCallInterface) {
throw Cr.NS_ERROR_UNEXPECTED;
}
return dataCallInterface;
},
// nsIGonkDataCallInterfaceService
notifyDataCallListChanged: function(aClientId, aCount, aDataCalls) {
let dataCallInterface = this.getDataCallInterface(aClientId);
dataCallInterface.handleDataCallListChanged(aCount, aDataCalls);
},
// nsIObserver
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case TOPIC_PREF_CHANGED:
if (aData === PREF_RIL_DEBUG_ENABLED) {
updateDebugFlag();
}
break;
case TOPIC_XPCOM_SHUTDOWN:
Services.prefs.removeObserver(PREF_RIL_DEBUG_ENABLED, this);
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
break;
}
},
};
function DataCallInterface(aClientId) {
this._clientId = aClientId;
this._radioInterface = gRil.getRadioInterface(aClientId);
this._listeners = [];
if (DEBUG) this.debug("DataCallInterface: " + aClientId);
}
DataCallInterface.prototype = {
classID: DATACALLINTERFACE_CID,
classInfo: XPCOMUtils.generateCI({classID: DATACALLINTERFACE_CID,
contractID: DATACALLINTERFACE_CONTRACTID,
classDescription: "Data Call Interface",
interfaces: [Ci.nsIDataCallInterface]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterface]),
debug: function(aMessage) {
dump("-*- DataCallInterface[" + this._clientId + "]: " + aMessage + "\n");
},
_clientId: -1,
_radioInterface: null,
_listeners: null,
// nsIDataCallInterface
setupDataCall: function(aApn, aUsername, aPassword, aAuthType, aPdpType,
aCallback) {
let connection =
gMobileConnectionService.getItemByServiceId(this._clientId);
let dataInfo = connection && connection.data;
let radioTechType = dataInfo.type;
let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
// Convert pdp type into string value.
let pdpType = RIL.RIL_DATACALL_PDP_TYPES[aPdpType];
this._radioInterface.sendWorkerMessage("setupDataCall", {
radioTech: radioTechnology,
apn: aApn,
user: aUsername,
passwd: aPassword,
chappap: aAuthType,
pdptype: pdpType
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
let dataCall = new DataCall(aResponse);
aCallback.notifySetupDataCallSuccess(dataCall);
}
});
},
deactivateDataCall: function(aCid, aReason, aCallback) {
this._radioInterface.sendWorkerMessage("deactivateDataCall", {
cid: aCid,
reason: aReason
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
aCallback.notifySuccess();
}
});
},
getDataCallList: function(aCallback) {
this._radioInterface.sendWorkerMessage("getDataCallList", null,
(aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
let dataCalls = aResponse.datacalls.map(
dataCall => new DataCall(dataCall));
aCallback.notifyGetDataCallListSuccess(dataCalls.length, dataCalls);
}
});
},
setDataRegistration: function(aAttach, aCallback) {
this._radioInterface.sendWorkerMessage("setDataRegistration", {
attach: aAttach
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
aCallback.notifySuccess();
}
});
},
handleDataCallListChanged: function(aCount, aDataCalls) {
this._notifyAllListeners("notifyDataCallListChanged", [aCount, aDataCalls]);
},
_notifyAllListeners: function(aMethodName, aArgs) {
let listeners = this._listeners.slice();
for (let listener of listeners) {
if (this._listeners.indexOf(listener) == -1) {
// Listener has been unregistered in previous run.
continue;
}
let handler = listener[aMethodName];
try {
handler.apply(listener, aArgs);
} catch (e) {
if (DEBUG) {
this.debug("listener for " + aMethodName + " threw an exception: " + e);
}
}
}
},
registerListener: function(aListener) {
if (this._listeners.indexOf(aListener) >= 0) {
return;
}
this._listeners.push(aListener);
},
unregisterListener: function(aListener) {
let index = this._listeners.indexOf(aListener);
if (index >= 0) {
this._listeners.splice(index, 1);
}
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataCallInterfaceService]);

View file

@ -0,0 +1,6 @@
# 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/.
component {e23e9337-592d-40b9-8cef-7bd47c28b72e} DataCallInterfaceService.js
contract @mozilla.org/datacall/interfaceservice;1 {e23e9337-592d-40b9-8cef-7bd47c28b72e}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
# DataCallManager.js
component {35b9efa2-e42c-45ce-8210-0a13e6f4aadc} DataCallManager.js
contract @mozilla.org/datacall/manager;1 {35b9efa2-e42c-45ce-8210-0a13e6f4aadc}
category profile-after-change DataCallManager @mozilla.org/datacall/manager;1

View file

@ -0,0 +1,28 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "GeolocationUtil.h"
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
{
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
const double radsInDeg = M_PI / 180.0;
const double rNewLat = aLat * radsInDeg;
const double rNewLon = aLon * radsInDeg;
const double rOldLat = aLastLat * radsInDeg;
const double rOldLon = aLastLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
if (cosDelta > 1.0) {
cosDelta = 1.0;
} else if (cosDelta < -1.0) {
cosDelta = -1.0;
}
return acos(cosDelta) * 6378137;
}

View file

@ -0,0 +1,13 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef GEOLOCATIONUTIL_H
#define GEOLOCATIONUTIL_H
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon);
#endif

View file

@ -0,0 +1,706 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GonkGPSGeolocationProvider.h"
#include <cmath>
#include <pthread.h>
#include <hardware/gps.h>
#include "base/task.h"
#include "GeolocationUtil.h"
#include "mozstumbler/MozStumbler.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsContentUtils.h"
#include "nsGeoPosition.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsINetworkInterface.h"
#include "nsIObserverService.h"
#include "nsJSUtils.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "prtime.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#ifdef AGPS_TYPE_INVALID
#define AGPS_HAVE_DUAL_APN
#endif
#define FLUSH_AIDE_DATA 0
#undef LOG
#undef ERR
#undef DBG
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkGPSGeolocationProvider", ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "GonkGPSGeolocationProvider", ## args)
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "GonkGPSGeolocationProvider" , ## args)
using namespace mozilla;
using namespace mozilla::dom;
static const int kDefaultPeriod = 1000; // ms
static bool gDebug_isLoggingEnabled = false;
static bool gDebug_isGPSLocationIgnored = false;
static const char* kMozSettingsChangedTopic = "mozsettings-changed";
// Both of these settings can be toggled in the Gaia Developer settings screen.
static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
// While most methods of GonkGPSGeolocationProvider should only be
// called from main thread, we deliberately put the Init and ShutdownGPS
// methods off main thread to avoid blocking.
NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider,
nsIGeolocationProvider,
nsIObserver,
nsISettingsServiceCallback)
/* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr;
GpsCallbacks GonkGPSGeolocationProvider::mCallbacks;
void
GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
{
if (gDebug_isGPSLocationIgnored) {
return;
}
class UpdateLocationEvent : public Runnable {
public:
UpdateLocationEvent(nsGeoPosition* aPosition)
: mPosition(aPosition)
{}
NS_IMETHOD Run() override {
RefPtr<GonkGPSGeolocationProvider> provider =
GonkGPSGeolocationProvider::GetSingleton();
nsCOMPtr<nsIGeolocationUpdate> callback = provider->mLocationCallback;
provider->mLastGPSPosition = mPosition;
if (callback) {
callback->Update(mPosition);
}
return NS_OK;
}
private:
RefPtr<nsGeoPosition> mPosition;
};
MOZ_ASSERT(location);
const float kImpossibleAccuracy_m = 0.001;
if (location->accuracy < kImpossibleAccuracy_m) {
return;
}
RefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
location->longitude,
location->altitude,
location->accuracy,
location->accuracy,
location->bearing,
location->speed,
PR_Now() / PR_USEC_PER_MSEC);
// Note above: Can't use location->timestamp as the time from the satellite is a
// minimum of 16 secs old (see http://leapsecond.com/java/gpsclock.htm).
// All code from this point on expects the gps location to be timestamped with the
// current time, most notably: the geolocation service which respects maximumAge
// set in the DOM JS.
if (gDebug_isLoggingEnabled) {
DBG("geo: GPS got a fix (%f, %f). accuracy: %f",
location->latitude,
location->longitude,
location->accuracy);
}
RefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
NS_DispatchToMainThread(event);
}
class NotifyObserversGPSTask final : public Runnable
{
public:
explicit NotifyObserversGPSTask(const char16_t* aData)
: mData(aData)
{}
NS_IMETHOD Run() override {
RefPtr<nsIGeolocationProvider> provider =
GonkGPSGeolocationProvider::GetSingleton();
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
obsService->NotifyObservers(provider, "geolocation-device-events", mData);
return NS_OK;
}
private:
const char16_t* mData;
};
void
GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
{
const char* msgStream=0;
switch (status->status) {
case GPS_STATUS_NONE:
msgStream = "geo: GPS_STATUS_NONE\n";
break;
case GPS_STATUS_SESSION_BEGIN:
msgStream = "geo: GPS_STATUS_SESSION_BEGIN\n";
break;
case GPS_STATUS_SESSION_END:
msgStream = "geo: GPS_STATUS_SESSION_END\n";
break;
case GPS_STATUS_ENGINE_ON:
msgStream = "geo: GPS_STATUS_ENGINE_ON\n";
NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSStarting"));
break;
case GPS_STATUS_ENGINE_OFF:
msgStream = "geo: GPS_STATUS_ENGINE_OFF\n";
NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSShutdown"));
break;
default:
msgStream = "geo: Unknown GPS status\n";
break;
}
if (gDebug_isLoggingEnabled){
DBG("%s", msgStream);
}
}
void
GonkGPSGeolocationProvider::SvStatusCallback(GpsSvStatus* sv_info)
{
if (gDebug_isLoggingEnabled) {
static int numSvs = 0;
static uint32_t numEphemeris = 0;
static uint32_t numAlmanac = 0;
static uint32_t numUsedInFix = 0;
unsigned int i = 1;
uint32_t svAlmanacCount = 0;
for (i = 1; i > 0; i <<= 1) {
if (i & sv_info->almanac_mask) {
svAlmanacCount++;
}
}
uint32_t svEphemerisCount = 0;
for (i = 1; i > 0; i <<= 1) {
if (i & sv_info->ephemeris_mask) {
svEphemerisCount++;
}
}
uint32_t svUsedCount = 0;
for (i = 1; i > 0; i <<= 1) {
if (i & sv_info->used_in_fix_mask) {
svUsedCount++;
}
}
// Log the message only if the the status changed.
if (sv_info->num_svs != numSvs ||
svAlmanacCount != numAlmanac ||
svEphemerisCount != numEphemeris ||
svUsedCount != numUsedInFix) {
LOG(
"geo: Number of SVs have (visibility, almanac, ephemeris): (%d, %d, %d)."
" %d of these SVs were used in fix.\n",
sv_info->num_svs, svAlmanacCount, svEphemerisCount, svUsedCount);
numSvs = sv_info->num_svs;
numAlmanac = svAlmanacCount;
numEphemeris = svEphemerisCount;
numUsedInFix = svUsedCount;
}
}
}
void
GonkGPSGeolocationProvider::NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length)
{
if (gDebug_isLoggingEnabled) {
DBG("NMEA: timestamp:\t%lld, length: %d, %s", timestamp, length, nmea);
}
}
void
GonkGPSGeolocationProvider::SetCapabilitiesCallback(uint32_t capabilities)
{
class UpdateCapabilitiesEvent : public Runnable {
public:
UpdateCapabilitiesEvent(uint32_t aCapabilities)
: mCapabilities(aCapabilities)
{}
NS_IMETHOD Run() override {
RefPtr<GonkGPSGeolocationProvider> provider =
GonkGPSGeolocationProvider::GetSingleton();
provider->mSupportsScheduling = mCapabilities & GPS_CAPABILITY_SCHEDULING;
provider->mSupportsSingleShot = mCapabilities & GPS_CAPABILITY_SINGLE_SHOT;
#ifdef GPS_CAPABILITY_ON_DEMAND_TIME
provider->mSupportsTimeInjection = mCapabilities & GPS_CAPABILITY_ON_DEMAND_TIME;
#endif
return NS_OK;
}
private:
uint32_t mCapabilities;
};
NS_DispatchToMainThread(new UpdateCapabilitiesEvent(capabilities));
}
void
GonkGPSGeolocationProvider::AcquireWakelockCallback()
{
}
void
GonkGPSGeolocationProvider::ReleaseWakelockCallback()
{
}
typedef void *(*pthread_func)(void *);
/** Callback for creating a thread that can call into the JS codes.
*/
pthread_t
GonkGPSGeolocationProvider::CreateThreadCallback(const char* name, void (*start)(void *), void* arg)
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
/* Unfortunately pthread_create and the callback disagreed on what
* start function should return.
*/
pthread_create(&thread, &attr, reinterpret_cast<pthread_func>(start), arg);
return thread;
}
void
GonkGPSGeolocationProvider::RequestUtcTimeCallback()
{
}
GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
: mStarted(false)
, mSupportsScheduling(false)
, mObservingSettingsChange(false)
, mSupportsSingleShot(false)
, mSupportsTimeInjection(false)
, mGpsInterface(nullptr)
{
}
GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mStarted, "Must call Shutdown before destruction");
sSingleton = nullptr;
}
already_AddRefed<GonkGPSGeolocationProvider>
GonkGPSGeolocationProvider::GetSingleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton)
sSingleton = new GonkGPSGeolocationProvider();
RefPtr<GonkGPSGeolocationProvider> provider = sSingleton;
return provider.forget();
}
const GpsInterface*
GonkGPSGeolocationProvider::GetGPSInterface()
{
hw_module_t* module;
if (hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module))
return nullptr;
hw_device_t* device;
if (module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device))
return nullptr;
gps_device_t* gps_device = (gps_device_t *)device;
const GpsInterface* result = gps_device->get_gps_interface(gps_device);
if (result->size != sizeof(GpsInterface)) {
return nullptr;
}
return result;
}
void
GonkGPSGeolocationProvider::RequestSettingValue(const char* aKey)
{
MOZ_ASSERT(aKey);
nsCOMPtr<nsISettingsService> ss = do_GetService("@mozilla.org/settingsService;1");
if (!ss) {
MOZ_ASSERT(ss);
return;
}
nsCOMPtr<nsISettingsServiceLock> lock;
nsresult rv = ss->CreateLock(nullptr, getter_AddRefs(lock));
if (NS_FAILED(rv)) {
ERR("error while createLock setting '%s': %d\n", aKey, uint32_t(rv));
return;
}
rv = lock->Get(aKey, this);
if (NS_FAILED(rv)) {
ERR("error while get setting '%s': %d\n", aKey, uint32_t(rv));
return;
}
}
void
GonkGPSGeolocationProvider::InjectLocation(double latitude,
double longitude,
float accuracy)
{
if (gDebug_isLoggingEnabled) {
DBG("injecting location (%f, %f) accuracy: %f", latitude, longitude, accuracy);
}
MOZ_ASSERT(NS_IsMainThread());
if (!mGpsInterface) {
return;
}
mGpsInterface->inject_location(latitude, longitude, accuracy);
}
void
GonkGPSGeolocationProvider::Init()
{
// Must not be main thread. Some GPS driver's first init takes very long.
MOZ_ASSERT(!NS_IsMainThread());
mGpsInterface = GetGPSInterface();
if (!mGpsInterface) {
return;
}
if (!mCallbacks.size) {
mCallbacks.size = sizeof(GpsCallbacks);
mCallbacks.location_cb = LocationCallback;
mCallbacks.status_cb = StatusCallback;
mCallbacks.sv_status_cb = SvStatusCallback;
mCallbacks.nmea_cb = NmeaCallback;
mCallbacks.set_capabilities_cb = SetCapabilitiesCallback;
mCallbacks.acquire_wakelock_cb = AcquireWakelockCallback;
mCallbacks.release_wakelock_cb = ReleaseWakelockCallback;
mCallbacks.create_thread_cb = CreateThreadCallback;
#ifdef GPS_CAPABILITY_ON_DEMAND_TIME
mCallbacks.request_utc_time_cb = RequestUtcTimeCallback;
#endif
}
if (mGpsInterface->init(&mCallbacks) != 0) {
return;
}
NS_DispatchToMainThread(NewRunnableMethod(this, &GonkGPSGeolocationProvider::StartGPS));
}
void
GonkGPSGeolocationProvider::StartGPS()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGpsInterface);
int32_t update = Preferences::GetInt("geo.default.update", kDefaultPeriod);
int positionMode = GPS_POSITION_MODE_STANDALONE;
if (!mSupportsScheduling) {
update = kDefaultPeriod;
}
mGpsInterface->set_position_mode(positionMode,
GPS_POSITION_RECURRENCE_PERIODIC,
update, 0, 0);
#if FLUSH_AIDE_DATA
// Delete cached data
mGpsInterface->delete_aiding_data(GPS_DELETE_ALL);
#endif
mGpsInterface->start();
}
NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate,
nsIGeolocationUpdate)
NS_IMETHODIMP
GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *position)
{
RefPtr<GonkGPSGeolocationProvider> provider =
GonkGPSGeolocationProvider::GetSingleton();
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
position->GetCoords(getter_AddRefs(coords));
if (!coords) {
return NS_ERROR_FAILURE;
}
double lat, lon, acc;
coords->GetLatitude(&lat);
coords->GetLongitude(&lon);
coords->GetAccuracy(&acc);
double delta = -1.0;
static double sLastMLSPosLat = 0;
static double sLastMLSPosLon = 0;
if (0 != sLastMLSPosLon || 0 != sLastMLSPosLat) {
delta = CalculateDeltaInMeter(lat, lon, sLastMLSPosLat, sLastMLSPosLon);
}
sLastMLSPosLat = lat;
sLastMLSPosLon = lon;
// if the MLS coord change is smaller than this arbitrarily small value
// assume the MLS coord is unchanged, and stick with the GPS location
const double kMinMLSCoordChangeInMeters = 10;
DOMTimeStamp time_ms = 0;
if (provider->mLastGPSPosition) {
provider->mLastGPSPosition->GetTimestamp(&time_ms);
}
const int64_t diff_ms = (PR_Now() / PR_USEC_PER_MSEC) - time_ms;
// We want to distinguish between the GPS being inactive completely
// and temporarily inactive. In the former case, we would use a low
// accuracy network location; in the latter, we only want a network
// location that appears to updating with movement.
const bool isGPSFullyInactive = diff_ms > 1000 * 60 * 2; // two mins
const bool isGPSTempInactive = diff_ms > 1000 * 10; // 10 secs
if (provider->mLocationCallback) {
if (isGPSFullyInactive ||
(isGPSTempInactive && delta > kMinMLSCoordChangeInMeters))
{
if (gDebug_isLoggingEnabled) {
DBG("Using MLS, GPS age:%fs, MLS Delta:%fm\n", diff_ms / 1000.0, delta);
}
provider->mLocationCallback->Update(position);
} else if (provider->mLastGPSPosition) {
if (gDebug_isLoggingEnabled) {
DBG("Using old GPS age:%fs\n", diff_ms / 1000.0);
}
// This is a fallback case so that the GPS provider responds with its last
// location rather than waiting for a more recent GPS or network location.
// The service decides if the location is too old, not the provider.
provider->mLocationCallback->Update(provider->mLastGPSPosition);
}
}
provider->InjectLocation(lat, lon, acc);
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::NetworkLocationUpdate::NotifyError(uint16_t error)
{
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::Startup()
{
MOZ_ASSERT(NS_IsMainThread());
if (mStarted) {
return NS_OK;
}
RequestSettingValue(kSettingDebugEnabled);
RequestSettingValue(kSettingDebugGpsIgnored);
// Setup an observer to watch changes to the setting.
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (observerService) {
MOZ_ASSERT(!mObservingSettingsChange);
nsresult rv = observerService->AddObserver(this, kMozSettingsChangedTopic, false);
if (NS_FAILED(rv)) {
NS_WARNING("geo: Gonk GPS AddObserver failed");
} else {
mObservingSettingsChange = true;
}
}
if (!mInitThread) {
nsresult rv = NS_NewThread(getter_AddRefs(mInitThread));
NS_ENSURE_SUCCESS(rv, rv);
}
mInitThread->Dispatch(NewRunnableMethod(this, &GonkGPSGeolocationProvider::Init),
NS_DISPATCH_NORMAL);
mNetworkLocationProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
if (mNetworkLocationProvider) {
nsresult rv = mNetworkLocationProvider->Startup();
if (NS_SUCCEEDED(rv)) {
RefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
mNetworkLocationProvider->Watch(update);
}
}
mStarted = true;
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
mLocationCallback = aCallback;
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStarted) {
return NS_OK;
}
mStarted = false;
if (mNetworkLocationProvider) {
mNetworkLocationProvider->Shutdown();
mNetworkLocationProvider = nullptr;
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
nsresult rv;
rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
if (NS_FAILED(rv)) {
NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
} else {
mObservingSettingsChange = false;
}
}
mInitThread->Dispatch(NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS),
NS_DISPATCH_NORMAL);
return NS_OK;
}
void
GonkGPSGeolocationProvider::ShutdownGPS()
{
MOZ_ASSERT(!mStarted, "Should only be called after Shutdown");
if (mGpsInterface) {
mGpsInterface->stop();
mGpsInterface->cleanup();
}
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::SetHighAccuracy(bool)
{
return NS_OK;
}
namespace {
int
ConvertToGpsNetworkType(int aNetworkInterfaceType)
{
switch (aNetworkInterfaceType) {
case nsINetworkInfo::NETWORK_TYPE_WIFI:
return AGPS_RIL_NETWORK_TYPE_WIFI;
case nsINetworkInfo::NETWORK_TYPE_MOBILE:
return AGPS_RIL_NETWORK_TYPE_MOBILE;
case nsINetworkInfo::NETWORK_TYPE_MOBILE_MMS:
return AGPS_RIL_NETWORK_TYPE_MOBILE_MMS;
case nsINetworkInfo::NETWORK_TYPE_MOBILE_SUPL:
return AGPS_RIL_NETWORK_TYPE_MOBILE_SUPL;
case nsINetworkInfo::NETWORK_TYPE_MOBILE_DUN:
return AGPS_RIL_NETWORK_TTYPE_MOBILE_DUN;
default:
NS_WARNING(nsPrintfCString("Unknown network type mapping %d",
aNetworkInterfaceType).get());
return -1;
}
}
} // namespace
NS_IMETHODIMP
GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(aTopic, kMozSettingsChangedTopic)) {
// Read changed setting value
RootedDictionary<SettingChangeNotification> setting(RootingCx());
if (!WrappedJSToDictionary(aSubject, setting)) {
return NS_OK;
}
if (setting.mKey.EqualsASCII(kSettingDebugGpsIgnored)) {
LOG("received mozsettings-changed: ignoring\n");
gDebug_isGPSLocationIgnored =
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
if (gDebug_isLoggingEnabled) {
DBG("GPS ignored %d\n", gDebug_isGPSLocationIgnored);
}
return NS_OK;
} else if (setting.mKey.EqualsASCII(kSettingDebugEnabled)) {
LOG("received mozsettings-changed: logging\n");
gDebug_isLoggingEnabled =
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
return NS_OK;
}
}
return NS_OK;
}
/** nsISettingsServiceCallback **/
NS_IMETHODIMP
GonkGPSGeolocationProvider::Handle(const nsAString& aName,
JS::Handle<JS::Value> aResult)
{
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage)
{
return NS_OK;
}

View file

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GonkGPSGeolocationProvider_h
#define GonkGPSGeolocationProvider_h
#include <hardware/gps.h> // for GpsInterface
#include "nsCOMPtr.h"
#include "nsIGeolocationProvider.h"
#include "nsIObserver.h"
#include "nsIDOMGeoPosition.h"
#include "nsISettingsService.h"
class nsIThread;
#define GONK_GPS_GEOLOCATION_PROVIDER_CID \
{ 0x48525ec5, 0x5a7f, 0x490a, { 0x92, 0x77, 0xba, 0x66, 0xe0, 0xd2, 0x2c, 0x8b } }
#define GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID \
"@mozilla.org/gonk-gps-geolocation-provider;1"
class GonkGPSGeolocationProvider : public nsIGeolocationProvider
, public nsIObserver
, public nsISettingsServiceCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIGEOLOCATIONPROVIDER
NS_DECL_NSIOBSERVER
NS_DECL_NSISETTINGSSERVICECALLBACK
static already_AddRefed<GonkGPSGeolocationProvider> GetSingleton();
private:
/* Client should use GetSingleton() to get the provider instance. */
GonkGPSGeolocationProvider();
GonkGPSGeolocationProvider(const GonkGPSGeolocationProvider &);
GonkGPSGeolocationProvider & operator = (const GonkGPSGeolocationProvider &);
virtual ~GonkGPSGeolocationProvider();
static void LocationCallback(GpsLocation* location);
static void StatusCallback(GpsStatus* status);
static void SvStatusCallback(GpsSvStatus* sv_info);
static void NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length);
static void SetCapabilitiesCallback(uint32_t capabilities);
static void AcquireWakelockCallback();
static void ReleaseWakelockCallback();
static pthread_t CreateThreadCallback(const char* name, void (*start)(void*), void* arg);
static void RequestUtcTimeCallback();
static GpsCallbacks mCallbacks;
void Init();
void StartGPS();
void ShutdownGPS();
void InjectLocation(double latitude, double longitude, float accuracy);
void RequestSettingValue(const char* aKey);
const GpsInterface* GetGPSInterface();
static GonkGPSGeolocationProvider* sSingleton;
bool mStarted;
bool mSupportsScheduling;
bool mObservingSettingsChange;
bool mSupportsSingleShot;
bool mSupportsTimeInjection;
const GpsInterface* mGpsInterface;
nsCOMPtr<nsIGeolocationUpdate> mLocationCallback;
nsCOMPtr<nsIThread> mInitThread;
nsCOMPtr<nsIGeolocationProvider> mNetworkLocationProvider;
nsCOMPtr<nsIDOMGeoPosition> mLastGPSPosition;
class NetworkLocationUpdate : public nsIGeolocationUpdate
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGEOLOCATIONUPDATE
NetworkLocationUpdate() {}
private:
virtual ~NetworkLocationUpdate() {}
};
};
#endif /* GonkGPSGeolocationProvider_h */

View file

@ -0,0 +1,56 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_mozmtpcommon_h__
#define mozilla_system_mozmtpcommon_h__
#include "mozilla/Types.h"
#include <android/log.h>
#define USE_DEBUG 0
#if USE_DEBUG
#define MTP_DBG(msg, ...) \
__android_log_print(ANDROID_LOG_DEBUG, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
#else
#define MTP_DBG(msg, ...)
#endif
#define MTP_LOG(msg, ...) \
__android_log_print(ANDROID_LOG_INFO, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
#define MTP_ERR(msg, ...) \
__android_log_print(ANDROID_LOG_ERROR, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
#define BEGIN_MTP_NAMESPACE \
namespace mozilla { namespace system { namespace mtp {
#define END_MTP_NAMESPACE \
} /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
#define USING_MTP_NAMESPACE \
using namespace mozilla::system::mtp;
namespace android {
class MOZ_EXPORT MtpServer;
class MOZ_EXPORT MtpStorage;
class MOZ_EXPORT MtpStringBuffer;
class MOZ_EXPORT MtpDatabase;
class MOZ_EXPORT MtpDataPacket;
class MOZ_EXPORT MtpProperty;
}
#include <mtp.h>
#include <MtpDatabase.h>
#include <MtpObjectInfo.h>
#include <MtpProperty.h>
#include <MtpServer.h>
#include <MtpStorage.h>
#include <MtpStringBuffer.h>
#include <MtpTypes.h>
#endif // mozilla_system_mtpcommon_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,288 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_mozmtpdatabase_h__
#define mozilla_system_mozmtpdatabase_h__
#include "MozMtpCommon.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIThread.h"
#include "nsTArray.h"
class DeviceStorageFile;
BEGIN_MTP_NAMESPACE // mozilla::system::mtp
class RefCountedMtpServer;
using namespace android;
class MozMtpDatabase final : public MtpDatabase
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpDatabase)
MozMtpDatabase();
// called from SendObjectInfo to reserve a database entry for the incoming file
virtual MtpObjectHandle beginSendObject(const char* aPath,
MtpObjectFormat aFormat,
MtpObjectHandle aParent,
MtpStorageID aStorageID,
uint64_t aSize,
time_t aModified);
// called to report success or failure of the SendObject file transfer
// success should signal a notification of the new object's creation,
// failure should remove the database entry created in beginSendObject
virtual void endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool aSucceeded);
virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual int getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual MtpObjectFormatList* getSupportedPlaybackFormats();
virtual MtpObjectFormatList* getSupportedCaptureFormats();
virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
virtual MtpDevicePropertyList* getSupportedDeviceProperties();
virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket);
virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo);
virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat);
virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences);
virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
MtpObjectFormat aFormat);
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
virtual void sessionStarted();
virtual void sessionEnded();
void AddStorage(MtpStorageID aStorageID, const char* aPath, const char *aName);
void RemoveStorage(MtpStorageID aStorageID);
void MtpWatcherUpdate(RefCountedMtpServer* aMtpServer,
DeviceStorageFile* aFile,
const nsACString& aEventType);
protected:
virtual ~MozMtpDatabase();
private:
struct DbEntry final
{
DbEntry()
: mHandle(0),
mStorageID(0),
mObjectFormat(MTP_FORMAT_DEFINED),
mParent(0),
mObjectSize(0),
mDateCreated(0),
mDateModified(0),
mDateAdded(0) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DbEntry)
MtpObjectHandle mHandle; // uint32_t
MtpStorageID mStorageID; // uint32_t
nsCString mObjectName;
MtpObjectFormat mObjectFormat; // uint16_t
MtpObjectHandle mParent; // uint32_t
uint64_t mObjectSize;
nsCString mDisplayName;
nsCString mPath;
time_t mDateCreated;
time_t mDateModified;
time_t mDateAdded;
protected:
~DbEntry() {}
};
template<class T>
class ProtectedTArray : private nsTArray<T>
{
public:
typedef T elem_type;
typedef typename nsTArray<T>::size_type size_type;
typedef typename nsTArray<T>::index_type index_type;
typedef nsTArray<T> base_type;
static const index_type NoIndex = base_type::NoIndex;
ProtectedTArray(mozilla::Mutex& aMutex)
: mMutex(aMutex)
{}
size_type Length() const
{
// GRR - This assert prints to stderr and won't show up in logcat.
mMutex.AssertCurrentThreadOwns();
return base_type::Length();
}
template <class Item>
elem_type* AppendElement(const Item& aItem)
{
mMutex.AssertCurrentThreadOwns();
return base_type::AppendElement(aItem);
}
void Clear()
{
mMutex.AssertCurrentThreadOwns();
base_type::Clear();
}
void RemoveElementAt(index_type aIndex)
{
mMutex.AssertCurrentThreadOwns();
base_type::RemoveElementAt(aIndex);
}
elem_type& operator[](index_type aIndex)
{
mMutex.AssertCurrentThreadOwns();
return base_type::ElementAt(aIndex);
}
const elem_type& operator[](index_type aIndex) const
{
mMutex.AssertCurrentThreadOwns();
return base_type::ElementAt(aIndex);
}
private:
mozilla::Mutex& mMutex;
};
typedef nsTArray<RefPtr<DbEntry> > UnprotectedDbArray;
typedef ProtectedTArray<RefPtr<DbEntry> > ProtectedDbArray;
struct StorageEntry final
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageEntry)
MtpStorageID mStorageID;
nsCString mStoragePath;
nsCString mStorageName;
protected:
~StorageEntry() {}
};
typedef ProtectedTArray<RefPtr<StorageEntry> > StorageArray;
enum MatchType
{
MatchAll,
MatchHandle,
MatchParent,
MatchFormat,
MatchHandleFormat,
MatchParentFormat,
};
bool IsValidHandle(MtpObjectHandle aHandle)
{
return aHandle > 0 && aHandle < mDb.Length();
}
void AddEntry(DbEntry* aEntry);
void AddEntryAndNotify(DbEntry* aEntr, RefCountedMtpServer* aMtpServer);
void DumpEntries(const char* aLabel);
MtpObjectHandle FindEntryByPath(const nsACString& aPath);
already_AddRefed<DbEntry> GetEntry(MtpObjectHandle aHandle);
void RemoveEntry(MtpObjectHandle aHandle);
void RemoveEntryAndNotify(MtpObjectHandle aHandle, RefCountedMtpServer* aMtpServer);
void UpdateEntry(MtpObjectHandle aHandle, DeviceStorageFile* aFile);
void UpdateEntryAndNotify(MtpObjectHandle aHandle, DeviceStorageFile* aFile,
RefCountedMtpServer* aMtpServer);
void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
uint32_t aMatchField2, UnprotectedDbArray& aResult);
nsCString BaseName(const nsCString& aPath);
MtpObjectHandle GetNextHandle()
{
return mDb.Length();
}
void AddDirectory(MtpStorageID aStorageID, const char *aPath, MtpObjectHandle aParent);
void CreateEntryForFileAndNotify(const nsACString& aPath,
DeviceStorageFile* aFile,
RefCountedMtpServer* aMtpServer);
StorageArray::index_type FindStorage(MtpStorageID aStorageID);
MtpStorageID FindStorageIDFor(const nsACString& aPath, nsCSubstring& aRemainder);
void MtpWatcherNotify(DbEntry* aEntry, const char* aEventType);
// We need a mutex to protext mDb and mStorage. The MTP server runs on a
// dedicated thread, and it updates/accesses mDb. When files are updated
// through DeviceStorage, we need to update/access mDb and mStorage as well
// (from a non-MTP server thread).
mozilla::Mutex mMutex;
ProtectedDbArray mDb;
StorageArray mStorage;
bool mBeginSendObjectCalled;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpdatabase_h__

View file

@ -0,0 +1,263 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "MozMtpServer.h"
#include "MozMtpDatabase.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
#include "base/message_loop.h"
#include "DeviceStorage.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Scoped.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsISupportsImpl.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "Volume.h"
#define DEFAULT_THREAD_TIMEOUT_MS 30000
using namespace android;
using namespace mozilla;
BEGIN_MTP_NAMESPACE
static const char* kMtpWatcherUpdate = "mtp-watcher-update";
class MtpWatcherUpdateRunnable final : public Runnable
{
public:
MtpWatcherUpdateRunnable(MozMtpDatabase* aMozMtpDatabase,
RefCountedMtpServer* aMtpServer,
DeviceStorageFile* aFile,
const nsACString& aEventType)
: mMozMtpDatabase(aMozMtpDatabase),
mMtpServer(aMtpServer),
mFile(aFile),
mEventType(aEventType)
{}
NS_IMETHOD Run() override
{
// Runs on the MtpWatcherUpdate->mIOThread
MOZ_ASSERT(!NS_IsMainThread());
mMozMtpDatabase->MtpWatcherUpdate(mMtpServer, mFile, mEventType);
return NS_OK;
}
private:
RefPtr<MozMtpDatabase> mMozMtpDatabase;
RefPtr<RefCountedMtpServer> mMtpServer;
RefPtr<DeviceStorageFile> mFile;
nsCString mEventType;
};
// The MtpWatcherUpdate class listens for mtp-watcher-update events
// and tells the MtpServer about changes made in device storage.
class MtpWatcherUpdate final : public nsIObserver
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
MtpWatcherUpdate(MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer)
{
MOZ_ASSERT(NS_IsMainThread());
mIOThread = new LazyIdleThread(
DEFAULT_THREAD_TIMEOUT_MS,
NS_LITERAL_CSTRING("MtpWatcherUpdate"));
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, kMtpWatcherUpdate, false);
}
NS_IMETHOD
Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (strcmp(aTopic, kMtpWatcherUpdate)) {
// We're only interested in mtp-watcher-update events
return NS_OK;
}
NS_ConvertUTF16toUTF8 eventType(aData);
if (!eventType.EqualsLiteral("modified") && !eventType.EqualsLiteral("deleted")) {
// Bug 1074604: Needn't handle "created" event, once file operation
// finished, it would trigger "modified" event.
return NS_OK;
}
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
file->Dump(kMtpWatcherUpdate);
MTP_LOG("%s: file %s %s", kMtpWatcherUpdate,
NS_LossyConvertUTF16toASCII(file->mPath).get(),
eventType.get());
RefPtr<MozMtpDatabase> mozMtpDatabase = mMozMtpServer->GetMozMtpDatabase();
RefPtr<RefCountedMtpServer> mtpServer = mMozMtpServer->GetMtpServer();
// We're not supposed to perform I/O on the main thread, so punt the
// notification (which will write to /dev/mtp_usb) to an I/O Thread.
RefPtr<MtpWatcherUpdateRunnable> r =
new MtpWatcherUpdateRunnable(mozMtpDatabase, mtpServer, file, eventType);
mIOThread->Dispatch(r, NS_DISPATCH_NORMAL);
return NS_OK;
}
protected:
~MtpWatcherUpdate()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(this, kMtpWatcherUpdate);
}
private:
RefPtr<MozMtpServer> mMozMtpServer;
nsCOMPtr<nsIThread> mIOThread;
};
NS_IMPL_ISUPPORTS(MtpWatcherUpdate, nsIObserver)
static StaticRefPtr<MtpWatcherUpdate> sMtpWatcherUpdate;
class AllocMtpWatcherUpdateRunnable final : public Runnable
{
public:
AllocMtpWatcherUpdateRunnable(MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer)
{}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
sMtpWatcherUpdate = new MtpWatcherUpdate(mMozMtpServer);
return NS_OK;
}
private:
RefPtr<MozMtpServer> mMozMtpServer;
};
class FreeMtpWatcherUpdateRunnable final : public Runnable
{
public:
FreeMtpWatcherUpdateRunnable(MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer)
{}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
sMtpWatcherUpdate = nullptr;
return NS_OK;
}
private:
RefPtr<MozMtpServer> mMozMtpServer;
};
class MtpServerRunnable : public Runnable
{
public:
MtpServerRunnable(int aMtpUsbFd, MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer),
mMtpUsbFd(aMtpUsbFd)
{
}
nsresult Run()
{
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
DebugOnly<nsresult> rv =
NS_DispatchToMainThread(new AllocMtpWatcherUpdateRunnable(mMozMtpServer));
MOZ_ASSERT(NS_SUCCEEDED(rv));
MTP_LOG("MozMtpServer started");
server->run();
MTP_LOG("MozMtpServer finished");
// server->run will have closed the file descriptor.
mMtpUsbFd.forget();
rv = NS_DispatchToMainThread(new FreeMtpWatcherUpdateRunnable(mMozMtpServer));
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NS_OK;
}
private:
RefPtr<MozMtpServer> mMozMtpServer;
ScopedClose mMtpUsbFd; // We want to hold this open while the server runs
};
already_AddRefed<RefCountedMtpServer>
MozMtpServer::GetMtpServer()
{
RefPtr<RefCountedMtpServer> server = mMtpServer;
return server.forget();
}
already_AddRefed<MozMtpDatabase>
MozMtpServer::GetMozMtpDatabase()
{
RefPtr<MozMtpDatabase> db = mMozMtpDatabase;
return db.forget();
}
bool
MozMtpServer::Init()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
const char *mtpUsbFilename = "/dev/mtp_usb";
mMtpUsbFd = open(mtpUsbFilename, O_RDWR);
if (mMtpUsbFd.get() < 0) {
MTP_ERR("open of '%s' failed((%s))", mtpUsbFilename, strerror(errno));
return false;
}
MTP_LOG("Opened '%s' fd %d", mtpUsbFilename, mMtpUsbFd.get());
mMozMtpDatabase = new MozMtpDatabase();
mMtpServer = new RefCountedMtpServer(mMtpUsbFd.get(), // fd
mMozMtpDatabase.get(), // MtpDatabase
false, // ptp?
AID_MEDIA_RW, // file group
0664, // file permissions
0775); // dir permissions
return true;
}
void
MozMtpServer::Run()
{
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
MOZ_ASSERT(mServerThread);
mServerThread->Dispatch(new MtpServerRunnable(mMtpUsbFd.forget(), this), NS_DISPATCH_NORMAL);
}
END_MTP_NAMESPACE

View file

@ -0,0 +1,61 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_mozmtpserver_h__
#define mozilla_system_mozmtpserver_h__
#include "MozMtpCommon.h"
#include "MozMtpDatabase.h"
#include "mozilla/FileUtils.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
BEGIN_MTP_NAMESPACE
using namespace android;
class RefCountedMtpServer : public MtpServer
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMtpServer)
RefCountedMtpServer(int aFd, MtpDatabase* aDatabase, bool aPtp,
int aFileGroup, int aFilePerm, int aDirectoryPerm)
: MtpServer(aFd, aDatabase, aPtp, aFileGroup, aFilePerm, aDirectoryPerm)
{
}
protected:
virtual ~RefCountedMtpServer() {}
};
class MozMtpServer
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpServer)
bool Init();
void Run();
already_AddRefed<RefCountedMtpServer> GetMtpServer();
already_AddRefed<MozMtpDatabase> GetMozMtpDatabase();
protected:
virtual ~MozMtpServer() {}
private:
RefPtr<RefCountedMtpServer> mMtpServer;
RefPtr<MozMtpDatabase> mMozMtpDatabase;
nsCOMPtr<nsIThread> mServerThread;
ScopedClose mMtpUsbFd;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpserver_h__

View file

@ -0,0 +1,135 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "MozMtpStorage.h"
#include "MozMtpDatabase.h"
#include "MozMtpServer.h"
#include "base/message_loop.h"
#include "nsXULAppAPI.h"
BEGIN_MTP_NAMESPACE
using namespace android;
MozMtpStorage::MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer),
mVolume(aVolume)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
// The MtpStorageID has the physical volume in the top 16 bits, and the
// logical volumein the lower 16 bits. We treat each volume as a separate
// phsyical storage;
mStorageID = mVolume->Id() << 16 | 1;
MTP_LOG("Storage constructed for Volume %s mStorageID 0x%08x",
aVolume->NameStr(), mStorageID);
Volume::RegisterVolumeObserver(this, "MozMtpStorage");
// Get things in sync
Notify(mVolume);
}
MozMtpStorage::~MozMtpStorage()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
MTP_LOG("Storage destructed for Volume %s mStorageID 0x%08x",
mVolume->NameStr(), mStorageID);
Volume::UnregisterVolumeObserver(this, "MozMtpStorage");
if (mMtpStorage) {
StorageUnavailable();
}
}
// virtual
void
MozMtpStorage::Notify(Volume* const& aVolume)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (aVolume != mVolume) {
// Not our volume
return;
}
Volume::STATE volState = aVolume->State();
MTP_LOG("Volume %s mStorageID 0x%08x state changed to %s SharingEnabled: %d",
aVolume->NameStr(), mStorageID, aVolume->StateStr(),
aVolume->IsSharingEnabled());
// vol->IsSharingEnabled really only applies to UMS volumes. We assume that
// that as long as MTP is enabled, then all volumes will be shared. The UI
// currently doesn't give us anything more granular than on/off.
if (mMtpStorage) {
if (volState != nsIVolume::STATE_MOUNTED) {
// The volume is no longer accessible. We need to remove this storage
// from the MTP server
StorageUnavailable();
}
} else {
if (volState == nsIVolume::STATE_MOUNTED) {
// The volume is accessible. Tell the MTP server.
StorageAvailable();
}
}
}
void
MozMtpStorage::StorageAvailable()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
nsCString mountPoint = mVolume->MountPoint();
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MozMtpDatabase",
mVolume->NameStr(), mStorageID, mountPoint.get());
RefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
db->AddStorage(mStorageID, mountPoint.get(), mVolume->NameStr());
MOZ_ASSERT(!mMtpStorage);
//TODO: Figure out what to do about maxFileSize.
mMtpStorage.reset(new MtpStorage(mStorageID, // id
mountPoint.get(), // filePath
mVolume->NameStr(), // description
1024uLL * 1024uLL, // reserveSpace
mVolume->IsHotSwappable(), // removable
2uLL * 1024uLL * 1024uLL * 1024uLL)); // maxFileSize
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MtpServer",
mVolume->NameStr(), mStorageID, mountPoint.get());
server->addStorage(mMtpStorage.get());
}
void
MozMtpStorage::StorageUnavailable()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
MOZ_ASSERT(mMtpStorage);
MTP_LOG("Removing mStorageID 0x%08x from MtpServer", mStorageID);
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
server->removeStorage(mMtpStorage.get());
MTP_LOG("Removing mStorageID 0x%08x from MozMtpDatabse", mStorageID);
RefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
db->RemoveStorage(mStorageID);
mMtpStorage = nullptr;
}
END_MTP_NAMESPACE

View file

@ -0,0 +1,47 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_mozmtpstorage_h__
#define mozilla_system_mozmtpstorage_h__
#include "MozMtpCommon.h"
#include "mozilla/UniquePtr.h"
#include "Volume.h"
BEGIN_MTP_NAMESPACE
using namespace android;
class MozMtpServer;
class MozMtpStorage : public Volume::EventObserver
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpStorage)
MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer);
typedef nsTArray<RefPtr<MozMtpStorage> > Array;
private:
virtual ~MozMtpStorage();
virtual void Notify(Volume* const& aEvent);
void StorageAvailable();
void StorageUnavailable();
RefPtr<MozMtpServer> mMozMtpServer;
UniquePtr<MtpStorage> mMtpStorage;
RefPtr<Volume> mVolume;
MtpStorageID mStorageID;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpstorage_h__

View file

@ -0,0 +1,68 @@
/* 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/. */
#include "NetIdManager.h"
NetIdManager::NetIdManager()
: mNextNetId(MIN_NET_ID)
{
}
int NetIdManager::getNextNetId()
{
// Modified from
// http://androidxref.com/5.0.0_r2/xref/frameworks/base/services/
// core/java/com/android/server/ConnectivityService.java#764
int netId = mNextNetId;
if (++mNextNetId > MAX_NET_ID) {
mNextNetId = MIN_NET_ID;
}
return netId;
}
void NetIdManager::acquire(const nsString& aInterfaceName,
NetIdInfo* aNetIdInfo)
{
// Lookup or create one.
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
aNetIdInfo->mNetId = getNextNetId();
aNetIdInfo->mRefCnt = 1;
} else {
aNetIdInfo->mRefCnt++;
}
// Update hash and return.
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
return;
}
bool NetIdManager::lookup(const nsString& aInterfaceName,
NetIdInfo* aNetIdInfo)
{
return mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo);
}
bool NetIdManager::release(const nsString& aInterfaceName,
NetIdInfo* aNetIdInfo)
{
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
return false; // No such key.
}
aNetIdInfo->mRefCnt--;
// Update the hash if still be referenced.
if (aNetIdInfo->mRefCnt > 0) {
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
return true;
}
// No longer be referenced. Remove the entry.
mInterfaceToNetIdHash.Remove(aInterfaceName);
return true;
}

View file

@ -0,0 +1,45 @@
/* 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/. */
#ifndef NetIdManager_h
#define NetIdManager_h
#include "nsString.h"
#include "nsDataHashtable.h"
// NetId is a logical network identifier defined by netd.
// A network is typically a physical one (i.e. PhysicalNetwork.cpp)
// for netd but it could be a virtual network as well.
// We currently use physical network only and use one-to-one
// network-interface mapping.
class NetIdManager {
public:
// keep in sync with system/netd/NetworkController.cpp
enum {
MIN_NET_ID = 100,
MAX_NET_ID = 65535,
};
// We need to count the number of references since different
// application like data and mms may use the same interface.
struct NetIdInfo {
int mNetId;
int mRefCnt;
};
public:
NetIdManager();
bool lookup(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
void acquire(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
bool release(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
private:
int getNextNetId();
int mNextNetId;
nsDataHashtable<nsStringHashKey, NetIdInfo> mInterfaceToNetIdHash;
};
#endif

View file

@ -0,0 +1,110 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const NETWORKLISTSERVICE_CONTRACTID =
"@mozilla.org/network/interface-list-service;1";
const NETWORKLISTSERVICE_CID =
Components.ID("{3780be6e-7012-4e53-ade6-15212fb88a0d}");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
function NetworkInterfaceListService () {
}
NetworkInterfaceListService.prototype = {
classID: NETWORKLISTSERVICE_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceListService]),
getDataInterfaceList: function(aConditions) {
return new NetworkInterfaceList(
cpmm.sendSyncMessage(
'NetworkInterfaceList:ListInterface',
{
excludeSupl: (aConditions &
Ci.nsINetworkInterfaceListService.
LIST_NOT_INCLUDE_SUPL_INTERFACES) != 0,
excludeMms: (aConditions &
Ci.nsINetworkInterfaceListService.
LIST_NOT_INCLUDE_MMS_INTERFACES) != 0,
excludeIms: (aConditions &
Ci.nsINetworkInterfaceListService.
LIST_NOT_INCLUDE_IMS_INTERFACES) != 0,
excludeDun: (aConditions &
Ci.nsINetworkInterfaceListService.
LIST_NOT_INCLUDE_DUN_INTERFACES) != 0,
excludeFota: (aConditions &
Ci.nsINetworkInterfaceListService.
LIST_NOT_INCLUDE_FOTA_INTERFACES) != 0
}
)[0]);
}
};
function FakeNetworkInfo(aAttributes) {
this.state = aAttributes.state;
this.type = aAttributes.type;
this.name = aAttributes.name;
this.ips = aAttributes.ips;
this.prefixLengths = aAttributes.prefixLengths;
this.gateways = aAttributes.gateways;
this.dnses = aAttributes.dnses;
}
FakeNetworkInfo.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]),
getAddresses: function (ips, prefixLengths) {
ips.value = this.ips.slice();
prefixLengths.value = this.prefixLengths.slice();
return this.ips.length;
},
getGateways: function (count) {
if (count) {
count.value = this.gateways.length;
}
return this.gateways.slice();
},
getDnses: function (count) {
if (count) {
count.value = this.dnses.length;
}
return this.dnses.slice();
}
};
function NetworkInterfaceList (aInterfaceLiterals) {
this._interfaces = [];
for (let entry of aInterfaceLiterals) {
this._interfaces.push(new FakeNetworkInfo(entry));
}
}
NetworkInterfaceList.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceList]),
getNumberOfInterface: function() {
return this._interfaces.length;
},
getInterfaceInfo: function(index) {
if (!this._interfaces) {
return null;
}
return this._interfaces[index];
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkInterfaceListService]);

View file

@ -0,0 +1,17 @@
# Copyright 2012 Mozilla Foundation and Mozilla contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# NetworkInterfaceListService.js
component {3780be6e-7012-4e53-ade6-15212fb88a0d} NetworkInterfaceListService.js
contract @mozilla.org/network/interface-list-service;1 {3780be6e-7012-4e53-ade6-15212fb88a0d}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
# NetworkManager.js
component {1ba9346b-53b5-4660-9dc6-58f0b258d0a6} NetworkManager.js
contract @mozilla.org/network/manager;1 {1ba9346b-53b5-4660-9dc6-58f0b258d0a6}

View file

@ -0,0 +1,862 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
const NETWORKSERVICE_CID = Components.ID("{48c13741-aec9-4a86-8962-432011708261}");
const TOPIC_PREF_CHANGED = "nsPref:changed";
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
"@mozilla.org/network/worker;1",
"nsINetworkWorker");
// 1xx - Requested action is proceeding
const NETD_COMMAND_PROCEEDING = 100;
// 2xx - Requested action has been successfully completed
const NETD_COMMAND_OKAY = 200;
// 4xx - The command is accepted but the requested action didn't
// take place.
const NETD_COMMAND_FAIL = 400;
// 5xx - The command syntax or parameters error
const NETD_COMMAND_ERROR = 500;
// 6xx - Unsolicited broadcasts
const NETD_COMMAND_UNSOLICITED = 600;
const WIFI_CTRL_INTERFACE = "wl0.1";
var debug;
function updateDebug() {
let debugPref = false; // set default value here.
try {
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
} catch (e) {}
if (debugPref) {
debug = function(s) {
dump("-*- NetworkService: " + s + "\n");
};
} else {
debug = function(s) {};
}
}
updateDebug();
function netdResponseType(aCode) {
return Math.floor(aCode / 100) * 100;
}
function isError(aCode) {
let type = netdResponseType(aCode);
return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY);
}
function Task(aId, aParams, aSetupFunction) {
this.id = aId;
this.params = aParams;
this.setupFunction = aSetupFunction;
}
function NetworkWorkerRequestQueue(aNetworkService) {
this.networkService = aNetworkService;
this.tasks = [];
}
NetworkWorkerRequestQueue.prototype = {
runQueue: function() {
if (this.tasks.length === 0) {
return;
}
let task = this.tasks[0];
debug("run task id: " + task.id);
if (typeof task.setupFunction === 'function') {
// If setupFunction returns false, skip sending to Network Worker but call
// handleWorkerMessage() directly with task id, as if the response was
// returned from Network Worker.
if (!task.setupFunction()) {
this.networkService.handleWorkerMessage({id: task.id});
return;
}
}
gNetworkWorker.postMessage(task.params);
},
enqueue: function(aId, aParams, aSetupFunction) {
debug("enqueue id: " + aId);
this.tasks.push(new Task(aId, aParams, aSetupFunction));
if (this.tasks.length === 1) {
this.runQueue();
}
},
dequeue: function(aId) {
debug("dequeue id: " + aId);
if (!this.tasks.length || this.tasks[0].id != aId) {
debug("Id " + aId + " is not on top of the queue");
return;
}
this.tasks.shift();
if (this.tasks.length > 0) {
// Run queue on the next tick.
Services.tm.currentThread.dispatch(() => {
this.runQueue();
}, Ci.nsIThread.DISPATCH_NORMAL);
}
}
};
/**
* This component watches for network interfaces changing state and then
* adjusts routes etc. accordingly.
*/
function NetworkService() {
debug("Starting NetworkService.");
let self = this;
if (gNetworkWorker) {
let networkListener = {
onEvent: function(aEvent) {
self.handleWorkerMessage(aEvent);
}
};
gNetworkWorker.start(networkListener);
}
// Callbacks to invoke when a reply arrives from the net_worker.
this.controlCallbacks = Object.create(null);
this.addedRoutes = new Map();
this.netWorkerRequestQueue = new NetworkWorkerRequestQueue(this);
this.shutdown = false;
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
}
NetworkService.prototype = {
classID: NETWORKSERVICE_CID,
classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID,
contractID: NETWORKSERVICE_CONTRACTID,
classDescription: "Network Service",
interfaces: [Ci.nsINetworkService]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService,
Ci.nsIObserver]),
addedRoutes: null,
shutdown: false,
// nsIObserver
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case TOPIC_PREF_CHANGED:
if (aData === PREF_NETWORK_DEBUG_ENABLED) {
updateDebug();
}
break;
case TOPIC_XPCOM_SHUTDOWN:
debug("NetworkService shutdown");
this.shutdown = true;
if (gNetworkWorker) {
gNetworkWorker.shutdown();
gNetworkWorker = null;
}
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
break;
}
},
// Helpers
idgen: 0,
controlMessage: function(aParams, aCallback, aSetupFunction) {
if (this.shutdown) {
return;
}
let id = this.idgen++;
aParams.id = id;
if (aCallback) {
this.controlCallbacks[id] = aCallback;
}
// For now, we use aSetupFunction to determine if this command needs to be
// queued or not.
if (aSetupFunction) {
this.netWorkerRequestQueue.enqueue(id, aParams, aSetupFunction);
return;
}
if (gNetworkWorker) {
gNetworkWorker.postMessage(aParams);
}
},
handleWorkerMessage: function(aResponse) {
debug("NetworkManager received message from worker: " + JSON.stringify(aResponse));
let id = aResponse.id;
if (aResponse.broadcast === true) {
Services.obs.notifyObservers(null, aResponse.topic, aResponse.reason);
return;
}
let callback = this.controlCallbacks[id];
if (callback) {
callback.call(this, aResponse);
delete this.controlCallbacks[id];
}
this.netWorkerRequestQueue.dequeue(id);
},
// nsINetworkService
getNetworkInterfaceStats: function(aInterfaceName, aCallback) {
debug("getNetworkInterfaceStats for " + aInterfaceName);
let file = new FileUtils.File("/proc/net/dev");
if (!file) {
aCallback.networkStatsAvailable(false, 0, 0, Date.now());
return;
}
NetUtil.asyncFetch({
uri: NetUtil.newURI(file),
loadUsingSystemPrincipal: true
}, function(inputStream, status) {
let rxBytes = 0,
txBytes = 0,
now = Date.now();
if (Components.isSuccessCode(status)) {
// Find record for corresponding interface.
let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/;
let data =
NetUtil.readInputStreamToString(inputStream, inputStream.available())
.split("\n");
for (let i = 2; i < data.length; i++) {
let parseResult = statExpr.exec(data[i]);
if (parseResult && parseResult[1] === aInterfaceName) {
rxBytes = parseInt(parseResult[2], 10);
txBytes = parseInt(parseResult[3], 10);
break;
}
}
}
// netd always return success even interface doesn't exist.
aCallback.networkStatsAvailable(true, rxBytes, txBytes, now);
});
},
setNetworkTetheringAlarm(aEnable, aInterface) {
// Method called when enabling disabling tethering, it checks if there is
// some alarm active and move from interfaceAlarm to globalAlarm because
// interfaceAlarm doens't work in tethering scenario due to forwarding.
debug("setNetworkTetheringAlarm for tethering" + aEnable);
let filename = aEnable ? "/proc/net/xt_quota/" + aInterface + "Alert" :
"/proc/net/xt_quota/globalAlert";
let file = new FileUtils.File(filename);
if (!file) {
return;
}
NetUtil.asyncFetch({
uri: NetUtil.newURI(file),
loadUsingSystemPrincipal: true
}, (inputStream, status) => {
if (Components.isSuccessCode(status)) {
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available())
.split("\n");
if (data) {
let threshold = parseInt(data[0], 10);
this._setNetworkTetheringAlarm(aEnable, aInterface, threshold);
}
}
});
},
_setNetworkTetheringAlarm(aEnable, aInterface, aThreshold, aCallback) {
debug("_setNetworkTetheringAlarm for tethering" + aEnable);
let cmd = aEnable ? "setTetheringAlarm" : "removeTetheringAlarm";
let params = {
cmd: cmd,
ifname: aInterface,
threshold: aThreshold,
};
this.controlMessage(params, function(aData) {
let code = aData.resultCode;
let reason = aData.resultReason;
let enableString = aEnable ? "Enable" : "Disable";
debug(enableString + " tethering Alarm result: Code " + code + " reason " + reason);
if (aCallback) {
aCallback.networkUsageAlarmResult(null);
}
});
},
setNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
if (!aInterfaceName) {
aCallback.networkUsageAlarmResult(-1);
return;
}
let self = this;
this._disableNetworkInterfaceAlarm(aInterfaceName, function(aResult) {
if (aThreshold < 0) {
if (!isError(aResult.resultCode)) {
aCallback.networkUsageAlarmResult(null);
return;
}
aCallback.networkUsageAlarmResult(aResult.reason);
return
}
// Check if tethering is enabled
let params = {
cmd: "getTetheringStatus"
};
self.controlMessage(params, function(aResult) {
if (isError(aResult.resultCode)) {
aCallback.networkUsageAlarmResult(aResult.reason);
return;
}
if (aResult.resultReason.indexOf('started') == -1) {
// Tethering disabled, set interfaceAlarm
self._setNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
return;
}
// Tethering enabled, set globalAlarm
self._setNetworkTetheringAlarm(true, aInterfaceName, aThreshold, aCallback);
});
});
},
_setNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
debug("setNetworkInterfaceAlarm for " + aInterfaceName + " at " + aThreshold + "bytes");
let params = {
cmd: "setNetworkInterfaceAlarm",
ifname: aInterfaceName,
threshold: aThreshold
};
params.report = true;
this.controlMessage(params, function(aResult) {
if (!isError(aResult.resultCode)) {
aCallback.networkUsageAlarmResult(null);
return;
}
this._enableNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
});
},
_enableNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
debug("enableNetworkInterfaceAlarm for " + aInterfaceName + " at " + aThreshold + "bytes");
let params = {
cmd: "enableNetworkInterfaceAlarm",
ifname: aInterfaceName,
threshold: aThreshold
};
params.report = true;
this.controlMessage(params, function(aResult) {
if (!isError(aResult.resultCode)) {
aCallback.networkUsageAlarmResult(null);
return;
}
aCallback.networkUsageAlarmResult(aResult.reason);
});
},
_disableNetworkInterfaceAlarm: function(aInterfaceName, aCallback) {
debug("disableNetworkInterfaceAlarm for " + aInterfaceName);
let params = {
cmd: "disableNetworkInterfaceAlarm",
ifname: aInterfaceName,
};
params.report = true;
this.controlMessage(params, function(aResult) {
aCallback(aResult);
});
},
setWifiOperationMode: function(aInterfaceName, aMode, aCallback) {
debug("setWifiOperationMode on " + aInterfaceName + " to " + aMode);
let params = {
cmd: "setWifiOperationMode",
ifname: aInterfaceName,
mode: aMode
};
params.report = true;
this.controlMessage(params, function(aResult) {
if (isError(aResult.resultCode)) {
aCallback.wifiOperationModeResult("netd command error");
} else {
aCallback.wifiOperationModeResult(null);
}
});
},
resetRoutingTable: function(aInterfaceName, aCallback) {
let options = {
cmd: "removeNetworkRoute",
ifname: aInterfaceName
};
this.controlMessage(options, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
setDNS: function(aInterfaceName, aDnsesCount, aDnses, aGatewaysCount,
aGateways, aCallback) {
debug("Going to set DNS to " + aInterfaceName);
let options = {
cmd: "setDNS",
ifname: aInterfaceName,
domain: "mozilla." + aInterfaceName + ".domain",
dnses: aDnses,
gateways: aGateways
};
this.controlMessage(options, function(aResult) {
aCallback.setDnsResult(aResult.success ? null : aResult.reason);
});
},
setDefaultRoute: function(aInterfaceName, aCount, aGateways, aCallback) {
debug("Going to change default route to " + aInterfaceName);
let options = {
cmd: "setDefaultRoute",
ifname: aInterfaceName,
gateways: aGateways
};
this.controlMessage(options, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
removeDefaultRoute: function(aInterfaceName, aCount, aGateways, aCallback) {
debug("Remove default route for " + aInterfaceName);
let options = {
cmd: "removeDefaultRoute",
ifname: aInterfaceName,
gateways: aGateways
};
this.controlMessage(options, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
_routeToString: function(aInterfaceName, aHost, aPrefixLength, aGateway) {
return aHost + "-" + aPrefixLength + "-" + aGateway + "-" + aInterfaceName;
},
modifyRoute: function(aAction, aInterfaceName, aHost, aPrefixLength, aGateway) {
let command;
switch (aAction) {
case Ci.nsINetworkService.MODIFY_ROUTE_ADD:
command = 'addHostRoute';
break;
case Ci.nsINetworkService.MODIFY_ROUTE_REMOVE:
command = 'removeHostRoute';
break;
default:
debug('Unknown action: ' + aAction);
return Promise.reject();
}
let route = this._routeToString(aInterfaceName, aHost, aPrefixLength, aGateway);
let setupFunc = () => {
let count = this.addedRoutes.get(route);
debug(command + ": " + route + " -> " + count);
// Return false if there is no need to send the command to network worker.
if ((aAction == Ci.nsINetworkService.MODIFY_ROUTE_ADD && count) ||
(aAction == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE &&
(!count || count > 1))) {
return false;
}
return true;
};
debug(command + " " + aHost + " on " + aInterfaceName);
let options = {
cmd: command,
ifname: aInterfaceName,
gateway: aGateway,
prefixLength: aPrefixLength,
ip: aHost
};
return new Promise((aResolve, aReject) => {
this.controlMessage(options, (aData) => {
let count = this.addedRoutes.get(route);
// Remove route from addedRoutes on success or failure.
if (aAction == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE) {
if (count > 1) {
this.addedRoutes.set(route, count - 1);
} else {
this.addedRoutes.delete(route);
}
}
if (aData.error) {
aReject(aData.reason);
return;
}
if (aAction == Ci.nsINetworkService.MODIFY_ROUTE_ADD) {
this.addedRoutes.set(route, count ? count + 1 : 1);
}
aResolve();
}, setupFunc);
});
},
addSecondaryRoute: function(aInterfaceName, aRoute, aCallback) {
debug("Going to add route to secondary table on " + aInterfaceName);
let options = {
cmd: "addSecondaryRoute",
ifname: aInterfaceName,
ip: aRoute.ip,
prefix: aRoute.prefix,
gateway: aRoute.gateway
};
this.controlMessage(options, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
removeSecondaryRoute: function(aInterfaceName, aRoute, aCallback) {
debug("Going to remove route from secondary table on " + aInterfaceName);
let options = {
cmd: "removeSecondaryRoute",
ifname: aInterfaceName,
ip: aRoute.ip,
prefix: aRoute.prefix,
gateway: aRoute.gateway
};
this.controlMessage(options, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
// Enable/Disable DHCP server.
setDhcpServer: function(aEnabled, aConfig, aCallback) {
if (null === aConfig) {
aConfig = {};
}
aConfig.cmd = "setDhcpServer";
aConfig.enabled = aEnabled;
this.controlMessage(aConfig, function(aResponse) {
if (!aResponse.success) {
aCallback.dhcpServerResult('Set DHCP server error');
return;
}
aCallback.dhcpServerResult(null);
});
},
// Enable/disable WiFi tethering by sending commands to netd.
setWifiTethering: function(aEnable, aConfig, aCallback) {
// config should've already contained:
// .ifname
// .internalIfname
// .externalIfname
aConfig.wifictrlinterfacename = WIFI_CTRL_INTERFACE;
aConfig.cmd = "setWifiTethering";
// The callback function in controlMessage may not be fired immediately.
this.controlMessage(aConfig, (aData) => {
let code = aData.resultCode;
let reason = aData.resultReason;
let enable = aData.enable;
let enableString = aEnable ? "Enable" : "Disable";
debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason);
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
if (isError(code)) {
aCallback.wifiTetheringEnabledChange("netd command error");
} else {
aCallback.wifiTetheringEnabledChange(null);
}
});
},
// Enable/disable USB tethering by sending commands to netd.
setUSBTethering: function(aEnable, aConfig, aCallback) {
aConfig.cmd = "setUSBTethering";
// The callback function in controlMessage may not be fired immediately.
this.controlMessage(aConfig, (aData) => {
let code = aData.resultCode;
let reason = aData.resultReason;
let enable = aData.enable;
let enableString = aEnable ? "Enable" : "Disable";
debug(enableString + " USB tethering result: Code " + code + " reason " + reason);
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
if (isError(code)) {
aCallback.usbTetheringEnabledChange("netd command error");
} else {
aCallback.usbTetheringEnabledChange(null);
}
});
},
// Switch usb function by modifying property of persist.sys.usb.config.
enableUsbRndis: function(aEnable, aCallback) {
debug("enableUsbRndis: " + aEnable);
let params = {
cmd: "enableUsbRndis",
enable: aEnable
};
// Ask net work to report the result when this value is set to true.
if (aCallback) {
params.report = true;
} else {
params.report = false;
}
// The callback function in controlMessage may not be fired immediately.
//this._usbTetheringAction = TETHERING_STATE_ONGOING;
this.controlMessage(params, function(aData) {
aCallback.enableUsbRndisResult(aData.result, aData.enable);
});
},
updateUpStream: function(aPrevious, aCurrent, aCallback) {
let params = {
cmd: "updateUpStream",
preInternalIfname: aPrevious.internalIfname,
preExternalIfname: aPrevious.externalIfname,
curInternalIfname: aCurrent.internalIfname,
curExternalIfname: aCurrent.externalIfname
};
this.controlMessage(params, function(aData) {
let code = aData.resultCode;
let reason = aData.resultReason;
debug("updateUpStream result: Code " + code + " reason " + reason);
aCallback.updateUpStreamResult(!isError(code), aData.curExternalIfname);
});
},
getInterfaces: function(callback) {
let params = {
cmd: "getInterfaces",
isAsync: true
};
this.controlMessage(params, function(data) {
debug("getInterfaces result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
callback.getInterfacesResult(success, data.interfaceList);
});
},
getInterfaceConfig: function(ifname, callback) {
let params = {
cmd: "getInterfaceConfig",
ifname: ifname,
isAsync: true
};
this.controlMessage(params, function(data) {
debug("getInterfaceConfig result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
let result = { ip: data.ipAddr,
prefix: data.prefixLength,
link: data.flag,
mac: data.macAddr };
callback.getInterfaceConfigResult(success, result);
});
},
setInterfaceConfig: function(config, callback) {
config.cmd = "setInterfaceConfig";
config.isAsync = true;
this.controlMessage(config, function(data) {
debug("setInterfaceConfig result: " + JSON.stringify(data));
let success = !isError(data.resultCode);
callback.setInterfaceConfigResult(success);
});
},
configureInterface: function(aConfig, aCallback) {
let params = {
cmd: "configureInterface",
ifname: aConfig.ifname,
ipaddr: aConfig.ipaddr,
mask: aConfig.mask,
gateway_long: aConfig.gateway,
dns1_long: aConfig.dns1,
dns2_long: aConfig.dns2,
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
dhcpRequest: function(aInterfaceName, aCallback) {
let params = {
cmd: "dhcpRequest",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.dhcpRequestResult(!aResult.error, aResult.error ? null : aResult);
});
},
stopDhcp: function(aInterfaceName, aCallback) {
let params = {
cmd: "stopDhcp",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
enableInterface: function(aInterfaceName, aCallback) {
let params = {
cmd: "enableInterface",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
disableInterface: function(aInterfaceName, aCallback) {
let params = {
cmd: "disableInterface",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
resetConnections: function(aInterfaceName, aCallback) {
let params = {
cmd: "resetConnections",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
createNetwork: function(aInterfaceName, aCallback) {
let params = {
cmd: "createNetwork",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
destroyNetwork: function(aInterfaceName, aCallback) {
let params = {
cmd: "destroyNetwork",
ifname: aInterfaceName
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
},
getNetId: function(aInterfaceName) {
let params = {
cmd: "getNetId",
ifname: aInterfaceName
};
return new Promise((aResolve, aReject) => {
this.controlMessage(params, result => {
if (result.error) {
aReject(result.reason);
return;
}
aResolve(result.netId);
});
});
},
setMtu: function (aInterfaceName, aMtu, aCallback) {
debug("Set MTU on " + aInterfaceName + ": " + aMtu);
let params = {
cmd: "setMtu",
ifname: aInterfaceName,
mtu: aMtu
};
this.controlMessage(params, function(aResult) {
aCallback.nativeCommandResult(!aResult.error);
});
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);

View file

@ -0,0 +1,3 @@
# NetworkService.js
component {48c13741-aec9-4a86-8962-432011708261} NetworkService.js
contract @mozilla.org/network/service;1 {48c13741-aec9-4a86-8962-432011708261}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,498 @@
/* 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/. */
#ifndef NetworkUtils_h
#define NetworkUtils_h
#include "nsAutoPtr.h"
#include "nsString.h"
#include "mozilla/dom/NetworkOptionsBinding.h"
#include "mozilla/dom/network/NetUtils.h"
#include "mozilla/ipc/Netd.h"
#include "nsTArray.h"
#include "NetIdManager.h"
class NetworkParams;
class CommandChain;
class CommandCallback {
public:
typedef void (*CallbackType)(CommandChain*, bool,
mozilla::dom::NetworkResultOptions& aResult);
typedef void (*CallbackWrapperType)(CallbackType aOriginalCallback,
CommandChain*, bool,
mozilla::dom::NetworkResultOptions& aResult);
CommandCallback()
: mCallback(nullptr)
, mCallbackWrapper(nullptr)
{
}
CommandCallback(CallbackType aCallback)
: mCallback(aCallback)
, mCallbackWrapper(nullptr)
{
}
CommandCallback(CallbackWrapperType aCallbackWrapper,
CommandCallback aOriginalCallback)
: mCallback(aOriginalCallback.mCallback)
, mCallbackWrapper(aCallbackWrapper)
{
}
void operator()(CommandChain* aChain, bool aError,
mozilla::dom::NetworkResultOptions& aResult)
{
if (mCallbackWrapper) {
return mCallbackWrapper(mCallback, aChain, aError, aResult);
}
if (mCallback) {
return mCallback(aChain, aError, aResult);
}
}
private:
CallbackType mCallback;
CallbackWrapperType mCallbackWrapper;
};
typedef void (*CommandFunc)(CommandChain*, CommandCallback,
mozilla::dom::NetworkResultOptions& aResult);
typedef void (*MessageCallback)(mozilla::dom::NetworkResultOptions& aResult);
typedef void (*ErrorCallback)(NetworkParams& aOptions,
mozilla::dom::NetworkResultOptions& aResult);
class NetworkParams
{
public:
NetworkParams() {
}
NetworkParams(const mozilla::dom::NetworkCommandOptions& aOther) {
#define COPY_SEQUENCE_FIELD(prop, type) \
if (aOther.prop.WasPassed()) { \
mozilla::dom::Sequence<type > const & currentValue = aOther.prop.InternalValue(); \
uint32_t length = currentValue.Length(); \
for (uint32_t idx = 0; idx < length; idx++) { \
prop.AppendElement(currentValue[idx]); \
} \
}
#define COPY_OPT_STRING_FIELD(prop, defaultValue) \
if (aOther.prop.WasPassed()) { \
if (aOther.prop.Value().EqualsLiteral("null")) { \
prop = defaultValue; \
} else { \
prop = aOther.prop.Value(); \
} \
} else { \
prop = defaultValue; \
}
#define COPY_OPT_FIELD(prop, defaultValue) \
if (aOther.prop.WasPassed()) { \
prop = aOther.prop.Value(); \
} else { \
prop = defaultValue; \
}
#define COPY_FIELD(prop) prop = aOther.prop;
COPY_FIELD(mId)
COPY_FIELD(mCmd)
COPY_OPT_STRING_FIELD(mDomain, EmptyString())
COPY_OPT_STRING_FIELD(mGateway, EmptyString())
COPY_SEQUENCE_FIELD(mGateways, nsString)
COPY_OPT_STRING_FIELD(mIfname, EmptyString())
COPY_OPT_STRING_FIELD(mIp, EmptyString())
COPY_OPT_FIELD(mPrefixLength, 0)
COPY_OPT_STRING_FIELD(mMode, EmptyString())
COPY_OPT_FIELD(mReport, false)
COPY_OPT_FIELD(mEnabled, false)
COPY_OPT_STRING_FIELD(mWifictrlinterfacename, EmptyString())
COPY_OPT_STRING_FIELD(mInternalIfname, EmptyString())
COPY_OPT_STRING_FIELD(mExternalIfname, EmptyString())
COPY_OPT_FIELD(mEnable, false)
COPY_OPT_STRING_FIELD(mSsid, EmptyString())
COPY_OPT_STRING_FIELD(mSecurity, EmptyString())
COPY_OPT_STRING_FIELD(mKey, EmptyString())
COPY_OPT_STRING_FIELD(mPrefix, EmptyString())
COPY_OPT_STRING_FIELD(mLink, EmptyString())
COPY_SEQUENCE_FIELD(mInterfaceList, nsString)
COPY_OPT_STRING_FIELD(mWifiStartIp, EmptyString())
COPY_OPT_STRING_FIELD(mWifiEndIp, EmptyString())
COPY_OPT_STRING_FIELD(mUsbStartIp, EmptyString())
COPY_OPT_STRING_FIELD(mUsbEndIp, EmptyString())
COPY_OPT_STRING_FIELD(mDns1, EmptyString())
COPY_OPT_STRING_FIELD(mDns2, EmptyString())
COPY_SEQUENCE_FIELD(mDnses, nsString)
COPY_OPT_STRING_FIELD(mStartIp, EmptyString())
COPY_OPT_STRING_FIELD(mEndIp, EmptyString())
COPY_OPT_STRING_FIELD(mServerIp, EmptyString())
COPY_OPT_STRING_FIELD(mMaskLength, EmptyString())
COPY_OPT_STRING_FIELD(mPreInternalIfname, EmptyString())
COPY_OPT_STRING_FIELD(mPreExternalIfname, EmptyString())
COPY_OPT_STRING_FIELD(mCurInternalIfname, EmptyString())
COPY_OPT_STRING_FIELD(mCurExternalIfname, EmptyString())
COPY_OPT_FIELD(mThreshold, -1)
COPY_OPT_FIELD(mIpaddr, 0)
COPY_OPT_FIELD(mMask, 0)
COPY_OPT_FIELD(mGateway_long, 0)
COPY_OPT_FIELD(mDns1_long, 0)
COPY_OPT_FIELD(mDns2_long, 0)
COPY_OPT_FIELD(mMtu, 0)
mLoopIndex = 0;
#undef COPY_SEQUENCE_FIELD
#undef COPY_OPT_STRING_FIELD
#undef COPY_OPT_FIELD
#undef COPY_FIELD
}
// Followings attributes are 1-to-1 mapping to NetworkCommandOptions.
int32_t mId;
nsString mCmd;
nsString mDomain;
nsString mGateway;
nsTArray<nsString> mGateways;
nsString mIfname;
nsString mIp;
uint32_t mPrefixLength;
nsString mMode;
bool mReport;
bool mEnabled;
nsString mWifictrlinterfacename;
nsString mInternalIfname;
nsString mExternalIfname;
bool mEnable;
nsString mSsid;
nsString mSecurity;
nsString mKey;
nsString mPrefix;
nsString mLink;
nsTArray<nsString> mInterfaceList;
nsString mWifiStartIp;
nsString mWifiEndIp;
nsString mUsbStartIp;
nsString mUsbEndIp;
nsString mDns1;
nsString mDns2;
nsTArray<nsString> mDnses;
nsString mStartIp;
nsString mEndIp;
nsString mServerIp;
nsString mMaskLength;
nsString mPreInternalIfname;
nsString mPreExternalIfname;
nsString mCurInternalIfname;
nsString mCurExternalIfname;
long long mThreshold;
long mIpaddr;
long mMask;
long mGateway_long;
long mDns1_long;
long mDns2_long;
long mMtu;
// Auxiliary information required to carry accros command chain.
int mNetId; // A locally defined id per interface.
uint32_t mLoopIndex; // Loop index for adding/removing multiple gateways.
};
// CommandChain store the necessary information to execute command one by one.
// Including :
// 1. Command parameters.
// 2. Command list.
// 3. Error callback function.
// 4. Index of current execution command.
class CommandChain final
{
public:
CommandChain(const NetworkParams& aParams,
const CommandFunc aCmds[],
uint32_t aLength,
ErrorCallback aError)
: mIndex(-1)
, mParams(aParams)
, mCommands(aCmds)
, mLength(aLength)
, mError(aError) {
}
NetworkParams&
getParams()
{
return mParams;
};
CommandFunc
getNextCommand()
{
mIndex++;
return mIndex < mLength ? mCommands[mIndex] : nullptr;
};
ErrorCallback
getErrorCallback() const
{
return mError;
};
private:
uint32_t mIndex;
NetworkParams mParams;
const CommandFunc* mCommands;
uint32_t mLength;
ErrorCallback mError;
};
// A helper class to easily construct a resolved
// or a pending result for command execution.
class CommandResult
{
public:
struct Pending {};
public:
CommandResult(int32_t aResultCode);
CommandResult(const mozilla::dom::NetworkResultOptions& aResult);
CommandResult(const Pending&);
bool isPending() const;
mozilla::dom::NetworkResultOptions mResult;
private:
bool mIsPending;
};
class NetworkUtils final
{
public:
NetworkUtils(MessageCallback aCallback);
~NetworkUtils();
void ExecuteCommand(NetworkParams aOptions);
void onNetdMessage(mozilla::ipc::NetdCommand* aCommand);
MessageCallback getMessageCallback() { return mMessageCallback; }
private:
/**
* Commands supported by NetworkUtils.
*/
CommandResult configureInterface(NetworkParams& aOptions);
CommandResult dhcpRequest(NetworkParams& aOptions);
CommandResult stopDhcp(NetworkParams& aOptions);
CommandResult enableInterface(NetworkParams& aOptions);
CommandResult disableInterface(NetworkParams& aOptions);
CommandResult resetConnections(NetworkParams& aOptions);
CommandResult setDefaultRoute(NetworkParams& aOptions);
CommandResult addHostRoute(NetworkParams& aOptions);
CommandResult removeDefaultRoute(NetworkParams& aOptions);
CommandResult removeHostRoute(NetworkParams& aOptions);
CommandResult removeNetworkRoute(NetworkParams& aOptions);
CommandResult setDNS(NetworkParams& aOptions);
CommandResult addSecondaryRoute(NetworkParams& aOptions);
CommandResult removeSecondaryRoute(NetworkParams& aOptions);
CommandResult setNetworkInterfaceAlarm(NetworkParams& aOptions);
CommandResult enableNetworkInterfaceAlarm(NetworkParams& aOptions);
CommandResult disableNetworkInterfaceAlarm(NetworkParams& aOptions);
CommandResult setTetheringAlarm(NetworkParams& aOptions);
CommandResult removeTetheringAlarm(NetworkParams& aOptions);
CommandResult getTetheringStatus(NetworkParams& aOptions);
CommandResult setWifiOperationMode(NetworkParams& aOptions);
CommandResult setDhcpServer(NetworkParams& aOptions);
CommandResult setWifiTethering(NetworkParams& aOptions);
CommandResult setUSBTethering(NetworkParams& aOptions);
CommandResult enableUsbRndis(NetworkParams& aOptions);
CommandResult updateUpStream(NetworkParams& aOptions);
CommandResult createNetwork(NetworkParams& aOptions);
CommandResult destroyNetwork(NetworkParams& aOptions);
CommandResult getNetId(NetworkParams& aOptions);
CommandResult setMtu(NetworkParams& aOptions);
CommandResult getInterfaces(NetworkParams& aOptions);
CommandResult getInterfaceConfig(NetworkParams& aOptions);
CommandResult setInterfaceConfig(NetworkParams& aOptions);
CommandResult addHostRouteLegacy(NetworkParams& aOptions);
CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);
/**
* function pointer array holds all netd commands should be executed
* in sequence to accomplish a given command by other module.
*/
static const CommandFunc sWifiEnableChain[];
static const CommandFunc sWifiDisableChain[];
static const CommandFunc sWifiFailChain[];
static const CommandFunc sWifiRetryChain[];
static const CommandFunc sWifiOperationModeChain[];
static const CommandFunc sUSBEnableChain[];
static const CommandFunc sUSBDisableChain[];
static const CommandFunc sUSBFailChain[];
static const CommandFunc sUpdateUpStreamChain[];
static const CommandFunc sStartDhcpServerChain[];
static const CommandFunc sStopDhcpServerChain[];
static const CommandFunc sNetworkInterfaceEnableAlarmChain[];
static const CommandFunc sNetworkInterfaceDisableAlarmChain[];
static const CommandFunc sNetworkInterfaceSetAlarmChain[];
static const CommandFunc sTetheringInterfaceSetAlarmChain[];
static const CommandFunc sTetheringInterfaceRemoveAlarmChain[];
static const CommandFunc sTetheringGetStatusChain[];
static const CommandFunc sGetInterfacesChain[];
static const CommandFunc sGetInterfaceConfigChain[];
static const CommandFunc sSetInterfaceConfigChain[];
/**
* Individual netd command stored in command chain.
*/
#define PARAMS CommandChain* aChain, CommandCallback aCallback, \
mozilla::dom::NetworkResultOptions& aResult
static void wifiFirmwareReload(PARAMS);
static void startAccessPointDriver(PARAMS);
static void stopAccessPointDriver(PARAMS);
static void setAccessPoint(PARAMS);
static void cleanUpStream(PARAMS);
static void createUpStream(PARAMS);
static void startSoftAP(PARAMS);
static void stopSoftAP(PARAMS);
static void clearWifiTetherParms(PARAMS);
static void enableAlarm(PARAMS);
static void disableAlarm(PARAMS);
static void setQuota(PARAMS);
static void removeQuota(PARAMS);
static void setAlarm(PARAMS);
static void removeAlarm(PARAMS);
static void setGlobalAlarm(PARAMS);
static void removeGlobalAlarm(PARAMS);
static void tetherInterface(PARAMS);
static void addInterfaceToLocalNetwork(PARAMS);
static void addRouteToLocalNetwork(PARAMS);
static void preTetherInterfaceList(PARAMS);
static void postTetherInterfaceList(PARAMS);
static void addUpstreamInterface(PARAMS);
static void removeUpstreamInterface(PARAMS);
static void setIpForwardingEnabled(PARAMS);
static void tetheringStatus(PARAMS);
static void stopTethering(PARAMS);
static void startTethering(PARAMS);
static void untetherInterface(PARAMS);
static void removeInterfaceFromLocalNetwork(PARAMS);
static void setDnsForwarders(PARAMS);
static void enableNat(PARAMS);
static void disableNat(PARAMS);
static void setDefaultInterface(PARAMS);
static void setInterfaceDns(PARAMS);
static void getInterfaceList(PARAMS);
static void getConfig(PARAMS);
static void setConfig(PARAMS);
static void wifiTetheringSuccess(PARAMS);
static void usbTetheringSuccess(PARAMS);
static void networkInterfaceAlarmSuccess(PARAMS);
static void updateUpStreamSuccess(PARAMS);
static void setDhcpServerSuccess(PARAMS);
static void wifiOperationModeSuccess(PARAMS);
static void clearAddrForInterface(PARAMS);
static void createNetwork(PARAMS);
static void destroyNetwork(PARAMS);
static void addInterfaceToNetwork(PARAMS);
static void addDefaultRouteToNetwork(PARAMS);
static void setDefaultNetwork(PARAMS);
static void removeDefaultRoute(PARAMS);
static void removeNetworkRouteSuccess(PARAMS);
static void removeNetworkRoute(PARAMS);
static void addRouteToInterface(PARAMS);
static void removeRouteFromInterface(PARAMS);
static void modifyRouteOnInterface(PARAMS, bool aDoAdd);
static void enableIpv6(PARAMS);
static void disableIpv6(PARAMS);
static void setMtu(PARAMS);
static void setIpv6Enabled(PARAMS, bool aEnabled);
static void addRouteToSecondaryTable(PARAMS);
static void removeRouteFromSecondaryTable(PARAMS);
static void defaultAsyncSuccessHandler(PARAMS);
static void getInterfacesSuccess(PARAMS);
static void getInterfaceConfigSuccess(PARAMS);
static void setInterfaceConfigSuccess(PARAMS);
#undef PARAMS
/**
* Error callback function executed when a command is fail.
*/
#define PARAMS NetworkParams& aOptions, \
mozilla::dom::NetworkResultOptions& aResult
static void wifiTetheringFail(PARAMS);
static void wifiOperationModeFail(PARAMS);
static void usbTetheringFail(PARAMS);
static void updateUpStreamFail(PARAMS);
static void setDhcpServerFail(PARAMS);
static void networkInterfaceAlarmFail(PARAMS);
static void setDnsFail(PARAMS);
static void defaultAsyncFailureHandler(PARAMS);
static void getInterfacesFail(PARAMS);
static void getInterfaceConfigFail(PARAMS);
static void setInterfaceConfigFail(PARAMS);
#undef PARAMS
/**
* Command chain processing functions.
*/
static void next(CommandChain* aChain, bool aError,
mozilla::dom::NetworkResultOptions& aResult);
static void nextNetdCommand();
static void doCommand(const char* aCommand, CommandChain* aChain, CommandCallback aCallback);
/**
* Notify broadcast message to main thread.
*/
void sendBroadcastMessage(uint32_t code, char* reason);
/**
* Utility functions.
*/
CommandResult checkUsbRndisState(NetworkParams& aOptions);
void dumpParams(NetworkParams& aOptions, const char* aType);
static void escapeQuote(nsCString& aString);
inline uint32_t netdResponseType(uint32_t code);
inline bool isBroadcastMessage(uint32_t code);
inline bool isError(uint32_t code);
inline bool isComplete(uint32_t code);
inline bool isProceeding(uint32_t code);
void Shutdown();
static void runNextQueuedCommandChain();
static void finalizeSuccess(CommandChain* aChain,
mozilla::dom::NetworkResultOptions& aResult);
template<size_t N>
static void runChain(const NetworkParams& aParams,
const CommandFunc (&aCmds)[N],
ErrorCallback aError);
static nsCString getSubnetIp(const nsCString& aIp, int aPrefixLength);
/**
* Callback function to send netd result to main thread.
*/
MessageCallback mMessageCallback;
/*
* Utility class to access libnetutils.
*/
nsAutoPtr<NetUtils> mNetUtils;
NetIdManager mNetIdManager;
};
#endif

View file

@ -0,0 +1,271 @@
/* 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/. */
#include "NetworkWorker.h"
#include "NetworkUtils.h"
#include <nsThreadUtils.h>
#include "mozilla/ModuleUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsAutoPtr.h"
#include "nsXULAppAPI.h"
#define NS_NETWORKWORKER_CID \
{ 0x6df093e1, 0x8127, 0x4fa7, {0x90, 0x13, 0xa3, 0xaa, 0xa7, 0x79, 0xbb, 0xdd} }
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
nsCOMPtr<nsIThread> gWorkerThread;
// The singleton network worker, to be used on the main thread.
StaticRefPtr<NetworkWorker> gNetworkWorker;
// The singleton networkutils class, that can be used on any thread.
static nsAutoPtr<NetworkUtils> gNetworkUtils;
// Runnable used dispatch command result on the main thread.
class NetworkResultDispatcher : public Runnable
{
public:
NetworkResultDispatcher(const NetworkResultOptions& aResult)
: mResult(aResult)
{
MOZ_ASSERT(!NS_IsMainThread());
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (gNetworkWorker) {
gNetworkWorker->DispatchNetworkResult(mResult);
}
return NS_OK;
}
private:
NetworkResultOptions mResult;
};
// Runnable used dispatch netd command on the worker thread.
class NetworkCommandDispatcher : public Runnable
{
public:
NetworkCommandDispatcher(const NetworkParams& aParams)
: mParams(aParams)
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread());
if (gNetworkUtils) {
gNetworkUtils->ExecuteCommand(mParams);
}
return NS_OK;
}
private:
NetworkParams mParams;
};
// Runnable used dispatch netd result on the worker thread.
class NetdEventRunnable : public Runnable
{
public:
NetdEventRunnable(NetdCommand* aCommand)
: mCommand(aCommand)
{
MOZ_ASSERT(!NS_IsMainThread());
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread());
if (gNetworkUtils) {
gNetworkUtils->onNetdMessage(mCommand);
}
return NS_OK;
}
private:
nsAutoPtr<NetdCommand> mCommand;
};
class NetdMessageConsumer : public NetdConsumer
{
public:
NetdMessageConsumer()
{
MOZ_ASSERT(NS_IsMainThread());
}
void MessageReceived(NetdCommand* aCommand)
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIRunnable> runnable = new NetdEventRunnable(aCommand);
if (gWorkerThread) {
gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
}
}
};
NS_IMPL_ISUPPORTS(NetworkWorker, nsINetworkWorker)
NetworkWorker::NetworkWorker()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gNetworkWorker);
}
NetworkWorker::~NetworkWorker()
{
MOZ_ASSERT(!gNetworkWorker);
MOZ_ASSERT(!mListener);
}
already_AddRefed<NetworkWorker>
NetworkWorker::FactoryCreate()
{
if (!XRE_IsParentProcess()) {
return nullptr;
}
MOZ_ASSERT(NS_IsMainThread());
if (!gNetworkWorker) {
gNetworkWorker = new NetworkWorker();
ClearOnShutdown(&gNetworkWorker);
gNetworkUtils = new NetworkUtils(NetworkWorker::NotifyResult);
ClearOnShutdown(&gNetworkUtils);
}
RefPtr<NetworkWorker> worker = gNetworkWorker.get();
return worker.forget();
}
NS_IMETHODIMP
NetworkWorker::Start(nsINetworkEventListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aListener);
if (mListener) {
return NS_OK;
}
nsresult rv;
rv = NS_NewNamedThread("NetworkWorker", getter_AddRefs(gWorkerThread));
if (NS_FAILED(rv)) {
NS_WARNING("Can't create network control thread");
return NS_ERROR_FAILURE;
}
StartNetd(new NetdMessageConsumer());
mListener = aListener;
return NS_OK;
}
NS_IMETHODIMP
NetworkWorker::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mListener) {
return NS_OK;
}
StopNetd();
gWorkerThread->Shutdown();
gWorkerThread = nullptr;
mListener = nullptr;
return NS_OK;
}
// Receive command from main thread (NetworkService.js).
NS_IMETHODIMP
NetworkWorker::PostMessage(JS::Handle<JS::Value> aOptions, JSContext* aCx)
{
MOZ_ASSERT(NS_IsMainThread());
NetworkCommandOptions options;
if (!options.Init(aCx, aOptions)) {
NS_WARNING("Bad dictionary passed to NetworkWorker::SendCommand");
return NS_ERROR_FAILURE;
}
// Dispatch the command to the control thread.
NetworkParams NetworkParams(options);
nsCOMPtr<nsIRunnable> runnable = new NetworkCommandDispatcher(NetworkParams);
if (gWorkerThread) {
gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
}
return NS_OK;
}
void
NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions)
{
MOZ_ASSERT(NS_IsMainThread());
mozilla::AutoSafeJSContext cx;
JS::RootedValue val(cx);
if (!ToJSValue(cx, aOptions, &val)) {
return;
}
// Call the listener with a JS value.
if (mListener) {
mListener->OnEvent(val);
}
}
// Callback function from network worker thread to update result on main thread.
void
NetworkWorker::NotifyResult(NetworkResultOptions& aResult)
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIRunnable> runnable = new NetworkResultDispatcher(aResult);
NS_DispatchToMainThread(runnable);
}
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NetworkWorker,
NetworkWorker::FactoryCreate)
NS_DEFINE_NAMED_CID(NS_NETWORKWORKER_CID);
static const mozilla::Module::CIDEntry kNetworkWorkerCIDs[] = {
{ &kNS_NETWORKWORKER_CID, false, nullptr, NetworkWorkerConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kNetworkWorkerContracts[] = {
{ "@mozilla.org/network/worker;1", &kNS_NETWORKWORKER_CID },
{ nullptr }
};
static const mozilla::Module kNetworkWorkerModule = {
mozilla::Module::kVersion,
kNetworkWorkerCIDs,
kNetworkWorkerContracts,
nullptr
};
} // namespace mozilla
NSMODULE_DEFN(NetworkWorkerModule) = &kNetworkWorkerModule;

View file

@ -0,0 +1,37 @@
/* 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/. */
#ifndef NetworkWorker_h
#define NetworkWorker_h
#include "mozilla/dom/NetworkOptionsBinding.h"
#include "mozilla/ipc/Netd.h"
#include "nsINetworkWorker.h"
#include "nsCOMPtr.h"
#include "nsThread.h"
namespace mozilla {
class NetworkWorker final : public nsINetworkWorker
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETWORKWORKER
static already_AddRefed<NetworkWorker> FactoryCreate();
void DispatchNetworkResult(const mozilla::dom::NetworkResultOptions& aOptions);
private:
NetworkWorker();
~NetworkWorker();
static void NotifyResult(mozilla::dom::NetworkResultOptions& aResult);
nsCOMPtr<nsINetworkEventListener> mListener;
};
} // namespace mozilla
#endif // NetworkWorker_h

View file

@ -0,0 +1,251 @@
/* 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/. */
#include "OpenFileFinder.h"
#include "mozilla/FileUtils.h"
#include "nsPrintfCString.h"
#include <sys/stat.h>
#include <errno.h>
#undef USE_DEBUG
#define USE_DEBUG 0
#undef LOG
#undef LOGW
#undef ERR
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args)
#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args)
#undef DBG
#if USE_DEBUG
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args)
#else
#define DBG(args...)
#endif
namespace mozilla {
namespace system {
OpenFileFinder::OpenFileFinder(const nsACString& aPath,
bool aCheckIsB2gOrDescendant /* = true */)
: mPath(aPath),
mProcDir(nullptr),
mFdDir(nullptr),
mPid(0),
mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant)
{
// We assume that we're running in the parent process
mMyPid = getpid();
}
OpenFileFinder::~OpenFileFinder()
{
Close();
}
bool
OpenFileFinder::First(OpenFileFinder::Info* aInfo)
{
Close();
mProcDir = opendir("/proc");
if (!mProcDir) {
return false;
}
mState = NEXT_PID;
return Next(aInfo);
}
bool
OpenFileFinder::Next(OpenFileFinder::Info* aInfo)
{
// NOTE: This function calls readdir and readlink, neither of which should
// block since we're using the proc filesystem, which is a purely
// kernel in-memory filesystem and doesn't depend on external driver
// behaviour.
while (mState != DONE) {
switch (mState) {
case NEXT_PID: {
struct dirent *pidEntry;
pidEntry = readdir(mProcDir);
if (!pidEntry) {
mState = DONE;
break;
}
char *endPtr;
mPid = strtol(pidEntry->d_name, &endPtr, 10);
if (mPid == 0 || *endPtr != '\0') {
// Not a +ve number - ignore
continue;
}
// We've found a /proc/PID directory. Scan open file descriptors.
if (mFdDir) {
closedir(mFdDir);
}
nsPrintfCString fdDirPath("/proc/%d/fd", mPid);
mFdDir = opendir(fdDirPath.get());
if (!mFdDir) {
continue;
}
mState = CHECK_FDS;
}
// Fall through
case CHECK_FDS: {
struct dirent *fdEntry;
while((fdEntry = readdir(mFdDir))) {
if (!strcmp(fdEntry->d_name, ".") ||
!strcmp(fdEntry->d_name, "..")) {
continue;
}
nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name);
nsCString resolvedPath;
if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) {
// We found an open file contained within the directory tree passed
// into the constructor.
FillInfo(aInfo, resolvedPath);
// If sCheckIsB2gOrDescendant is set false, the caller cares about
// all processes which have open files. If sCheckIsB2gOrDescendant
// is set false, we only care about the b2g proccess or its descendants.
if (!mCheckIsB2gOrDescendant || aInfo->mIsB2gOrDescendant) {
return true;
}
LOG("Ignore process(%d), not a b2g process or its descendant.",
aInfo->mPid);
}
}
// We've checked all of the files for this pid, move onto the next one.
mState = NEXT_PID;
continue;
}
case DONE:
default:
mState = DONE; // covers the default case
break;
}
}
return false;
}
void
OpenFileFinder::Close()
{
if (mFdDir) {
closedir(mFdDir);
}
if (mProcDir) {
closedir(mProcDir);
}
}
void
OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath)
{
aInfo->mFileName = aPath;
aInfo->mPid = mPid;
nsPrintfCString exePath("/proc/%d/exe", mPid);
ReadSymLink(exePath, aInfo->mExe);
aInfo->mComm.Truncate();
aInfo->mAppName.Truncate();
nsPrintfCString statPath("/proc/%d/stat", mPid);
nsCString statString;
statString.SetLength(200);
char *stat = statString.BeginWriting();
if (!stat) {
return;
}
ReadSysFile(statPath.get(), stat, statString.Length());
// The stat line includes the comm field, surrounded by parenthesis.
// However, the contents of the comm field itself is arbitrary and
// and can include ')', so we search for the rightmost ) as being
// the end of the comm field.
char *closeParen = strrchr(stat, ')');
if (!closeParen) {
return;
}
char *openParen = strchr(stat, '(');
if (!openParen) {
return;
}
if (openParen >= closeParen) {
return;
}
nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1);
aInfo->mComm = comm;
// There is a single character field after the comm and then
// the parent pid (the field we're interested in).
// ) X ppid
// 01234
int ppid = atoi(&closeParen[4]);
if (mPid == mMyPid) {
// This is chrome process
aInfo->mIsB2gOrDescendant = true;
DBG("Chrome process has open file(s)");
return;
}
// For the rest (non-chrome process), we recursively check the ppid to know
// it is a descendant of b2g or not. See bug 931456.
while (ppid != mMyPid && ppid != 1) {
DBG("Process(%d) is not forked from b2g(%d) or Init(1), keep looking",
ppid, mMyPid);
nsPrintfCString ppStatPath("/proc/%d/stat", ppid);
ReadSysFile(ppStatPath.get(), stat, statString.Length());
closeParen = strrchr(stat, ')');
if (!closeParen) {
return;
}
ppid = atoi(&closeParen[4]);
}
if (ppid == 1) {
// This is a not a b2g process.
DBG("Non-b2g process has open file(s)");
aInfo->mIsB2gOrDescendant = false;
return;
}
if (ppid == mMyPid) {
// This is a descendant of b2g.
DBG("Child process of chrome process has open file(s)");
aInfo->mIsB2gOrDescendant = true;
}
// This looks like a content process. The comm field will be the
// app name.
aInfo->mAppName = aInfo->mComm;
}
bool
OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath)
{
aOutPath.Truncate();
const char *symLink = aSymLink.BeginReading();
// Verify that we actually have a symlink.
struct stat st;
if (lstat(symLink, &st)) {
return false;
}
if ((st.st_mode & S_IFMT) != S_IFLNK) {
return false;
}
// Contrary to the documentation st.st_size doesn't seem to be a reliable
// indication of the length when reading from /proc, so we use a fixed
// size buffer instead.
char resolvedSymLink[PATH_MAX];
ssize_t pathLength = readlink(symLink, resolvedSymLink,
sizeof(resolvedSymLink) - 1);
if (pathLength <= 0) {
return false;
}
resolvedSymLink[pathLength] = '\0';
aOutPath.Assign(resolvedSymLink);
return true;
}
} // system
} // mozilla

View file

@ -0,0 +1,63 @@
/* 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/. */
#ifndef mozilla_system_openfilefinder_h__
#define mozilla_system_openfilefinder_h__
#include "nsString.h"
#include <dirent.h>
namespace mozilla {
namespace system {
class OpenFileFinder
{
public:
enum State
{
NEXT_PID,
CHECK_FDS,
DONE
};
class Info
{
public:
nsCString mFileName; // name of the the open file
nsCString mAppName; // App which has the file open (if it's a b2g app)
pid_t mPid; // pid of the process which has the file open
nsCString mComm; // comm associated with pid
nsCString mExe; // executable name associated with pid
bool mIsB2gOrDescendant; // this is b2g/its descendant or not
};
OpenFileFinder(const nsACString& aPath, bool aCheckIsB2gOrDescendant = true);
~OpenFileFinder();
bool First(Info* aInfo); // Return the first open file
bool Next(Info* aInfo); // Return the next open file
void Close();
private:
void FillInfo(Info *aInfo, const nsACString& aPath);
bool ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath);
bool PathMatches(const nsACString& aPath)
{
return Substring(aPath, 0, mPath.Length()).Equals(mPath);
}
State mState; // Keeps track of what we're doing.
nsCString mPath; // Only report files contained within this directory tree
DIR* mProcDir; // Used for scanning /proc
DIR* mFdDir; // Used for scanning /proc/PID/fd
int mPid; // PID currently being processed
pid_t mMyPid; // PID of parent process, we assume we're running on it.
bool mCheckIsB2gOrDescendant; // Do we care about non-b2g process?
};
} // system
} // mozilla
#endif // mozilla_system_nsvolume_h__

View file

@ -0,0 +1,338 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
let obj = {};
Cu.import("resource://gre/modules/ril_consts.js", obj);
return obj;
});
/**
* RILSystemMessenger
*/
this.RILSystemMessenger = function() {};
RILSystemMessenger.prototype = {
/**
* Hook of Broadcast function
*
* @param aType
* The type of the message to be sent.
* @param aMessage
* The message object to be broadcasted.
*/
broadcastMessage: function(aType, aMessage) {
// Function stub to be replaced by the owner of this messenger.
},
/**
* Hook of the function to create MozStkCommand message.
* @param aStkProactiveCmd
* nsIStkProactiveCmd instance.
*
* @return a JS object which complies the dictionary of MozStkCommand defined
* in MozStkCommandEvent.webidl
*/
createCommandMessage: function(aStkProactiveCmd) {
// Function stub to be replaced by the owner of this messenger.
},
/**
* Wrapper to send "telephony-new-call" system message.
*/
notifyNewCall: function() {
this.broadcastMessage("telephony-new-call", {});
},
/**
* Wrapper to send "telephony-call-ended" system message.
*/
notifyCallEnded: function(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
aDuration, aOutgoing, aHangUpLocal) {
let data = {
serviceId: aServiceId,
number: aNumber,
emergency: aEmergency,
duration: aDuration,
direction: aOutgoing ? "outgoing" : "incoming",
hangUpLocal: aHangUpLocal
};
if (aCdmaWaitingNumber != null) {
data.secondNumber = aCdmaWaitingNumber;
}
this.broadcastMessage("telephony-call-ended", data);
},
_convertSmsMessageClass: function(aMessageClass) {
return RIL.GECKO_SMS_MESSAGE_CLASSES[aMessageClass] || null;
},
_convertSmsDelivery: function(aDelivery) {
return ["received", "sending", "sent", "error"][aDelivery] || null;
},
_convertSmsDeliveryStatus: function(aDeliveryStatus) {
return [
RIL.GECKO_SMS_DELIVERY_STATUS_NOT_APPLICABLE,
RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS,
RIL.GECKO_SMS_DELIVERY_STATUS_PENDING,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR
][aDeliveryStatus] || null;
},
/**
* Wrapper to send 'sms-received', 'sms-delivery-success', 'sms-sent',
* 'sms-failed', 'sms-delivery-error' system message.
*/
notifySms: function(aNotificationType, aId, aThreadId, aIccId, aDelivery,
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead) {
let msgType = [
"sms-received",
"sms-sent",
"sms-delivery-success",
"sms-failed",
"sms-delivery-error"
][aNotificationType];
if (!msgType) {
throw new Error("Invalid Notification Type: " + aNotificationType);
}
this.broadcastMessage(msgType, {
iccId: aIccId,
type: "sms",
id: aId,
threadId: aThreadId,
delivery: this._convertSmsDelivery(aDelivery),
deliveryStatus: this._convertSmsDeliveryStatus(aDeliveryStatus),
sender: aSender,
receiver: aReceiver,
body: aBody,
messageClass: this._convertSmsMessageClass(aMessageClass),
timestamp: aTimestamp,
sentTimestamp: aSentTimestamp,
deliveryTimestamp: aDeliveryTimestamp,
read: aRead
});
},
_convertCbGsmGeographicalScope: function(aGeographicalScope) {
return RIL.CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[aGeographicalScope] || null;
},
_convertCbMessageClass: function(aMessageClass) {
return RIL.GECKO_SMS_MESSAGE_CLASSES[aMessageClass] || null;
},
_convertCbEtwsWarningType: function(aWarningType) {
return RIL.CB_ETWS_WARNING_TYPE_NAMES[aWarningType] || null;
},
/**
* Wrapper to send 'cellbroadcast-received' system message.
*/
notifyCbMessageReceived: function(aServiceId, aGsmGeographicalScope, aMessageCode,
aMessageId, aLanguage, aBody, aMessageClass,
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup) {
// Align the same layout to MozCellBroadcastMessage
let data = {
serviceId: aServiceId,
gsmGeographicalScope: this._convertCbGsmGeographicalScope(aGsmGeographicalScope),
messageCode: aMessageCode,
messageId: aMessageId,
language: aLanguage,
body: aBody,
messageClass: this._convertCbMessageClass(aMessageClass),
timestamp: aTimestamp,
cdmaServiceCategory: null,
etws: null
};
if (aHasEtwsInfo) {
data.etws = {
warningType: this._convertCbEtwsWarningType(aEtwsWarningType),
emergencyUserAlert: aEtwsEmergencyUserAlert,
popup: aEtwsPopup
};
}
if (aCdmaServiceCategory !=
Ci.nsICellBroadcastService.CDMA_SERVICE_CATEGORY_INVALID) {
data.cdmaServiceCategory = aCdmaServiceCategory;
}
this.broadcastMessage("cellbroadcast-received", data);
},
/**
* Wrapper to send 'ussd-received' system message.
*/
notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) {
this.broadcastMessage("ussd-received", {
serviceId: aServiceId,
message: aMessage,
sessionEnded: aSessionEnded
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Display Info.
*/
notifyCdmaInfoRecDisplay: function(aServiceId, aDisplay) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
display: aDisplay
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Called Party
* Number Info.
*/
notifyCdmaInfoRecCalledPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
calledNumber: {
type: aType,
plan: aPlan,
number: aNumber,
pi: aPi,
si: aSi
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Calling Party
* Number Info.
*/
notifyCdmaInfoRecCallingPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
callingNumber: {
type: aType,
plan: aPlan,
number: aNumber,
pi: aPi,
si: aSi
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Connected Party
* Number Info.
*/
notifyCdmaInfoRecConnectedPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
connectedNumber: {
type: aType,
plan: aPlan,
number: aNumber,
pi: aPi,
si: aSi
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Signal Info.
*/
notifyCdmaInfoRecSignal: function(aServiceId, aType, aAlertPitch, aSignal) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
signal: {
type: aType,
alertPitch: aAlertPitch,
signal: aSignal
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Redirecting
* Number Info.
*/
notifyCdmaInfoRecRedirectingNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi, aReason) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
redirect: {
type: aType,
plan: aPlan,
number: aNumber,
pi: aPi,
si: aSi,
reason: aReason
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Line Control Info.
*/
notifyCdmaInfoRecLineControl: function(aServiceId, aPolarityIncluded,
aToggle, aReverse, aPowerDenial) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
lineControl: {
polarityIncluded: aPolarityIncluded,
toggle: aToggle,
reverse: aReverse,
powerDenial: aPowerDenial
}
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with CLIR Info.
*/
notifyCdmaInfoRecClir: function(aServiceId, aCause) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
clirCause: aCause
});
},
/**
* Wrapper to send 'cdma-info-rec-received' system message with Audio Control Info.
*/
notifyCdmaInfoRecAudioControl: function(aServiceId, aUpLink, aDownLink) {
this.broadcastMessage("cdma-info-rec-received", {
clientId: aServiceId,
audioControl: {
upLink: aUpLink,
downLink: aDownLink
}
});
},
/**
* Wrapper to send 'icc-stkcommand' system message with Audio Control Info.
*/
notifyStkProactiveCommand: function(aIccId, aCommand) {
this.broadcastMessage("icc-stkcommand", {
iccId: aIccId,
command: this.createCommandMessage(aCommand)
});
}
};
this.EXPORTED_SYMBOLS = [
'RILSystemMessenger'
];

View file

@ -0,0 +1,169 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var RSM = {};
Cu.import("resource://gre/modules/RILSystemMessenger.jsm", RSM);
const RILSYSTEMMESSENGERHELPER_CONTRACTID =
"@mozilla.org/ril/system-messenger-helper;1";
const RILSYSTEMMESSENGERHELPER_CID =
Components.ID("{19d9a4ea-580d-11e4-8f6c-37ababfaaea9}");
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
"@mozilla.org/system-message-internal;1",
"nsISystemMessagesInternal");
XPCOMUtils.defineLazyServiceGetter(this, "gStkCmdFactory",
"@mozilla.org/icc/stkcmdfactory;1",
"nsIStkCmdFactory");
var DEBUG = false;
function debug(s) {
dump("-@- RILSystemMessenger: " + s + "\n");
};
// Read debug setting from pref.
try {
let debugPref = Services.prefs.getBoolPref("ril.debugging.enabled");
DEBUG = DEBUG || debugPref;
} catch (e) {}
/**
* RILSystemMessengerHelper
*/
function RILSystemMessengerHelper() {
this.messenger = new RSM.RILSystemMessenger();
this.messenger.broadcastMessage = (aType, aMessage) => {
if (DEBUG) {
debug("broadcastMessage: aType: " + aType +
", aMessage: "+ JSON.stringify(aMessage));
}
gSystemMessenger.broadcastMessage(aType, aMessage);
};
this.messenger.createCommandMessage = (aStkProactiveCmd) => {
return gStkCmdFactory.createCommandMessage(aStkProactiveCmd);
};
}
RILSystemMessengerHelper.prototype = {
classID: RILSYSTEMMESSENGERHELPER_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyMessenger,
Ci.nsISmsMessenger,
Ci.nsICellbroadcastMessenger,
Ci.nsIMobileConnectionMessenger,
Ci.nsIIccMessenger]),
/**
* RILSystemMessenger instance.
*/
messenger: null,
/**
* nsITelephonyMessenger API
*/
notifyNewCall: function() {
this.messenger.notifyNewCall();
},
notifyCallEnded: function(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
aDuration, aOutgoing, aHangUpLocal) {
this.messenger.notifyCallEnded(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
aDuration, aOutgoing, aHangUpLocal);
},
notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) {
this.messenger.notifyUssdReceived(aServiceId, aMessage, aSessionEnded);
},
/**
* nsISmsMessenger API
*/
notifySms: function(aNotificationType, aId, aThreadId, aIccId, aDelivery,
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead) {
this.messenger.notifySms(aNotificationType, aId, aThreadId, aIccId, aDelivery,
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead);
},
/**
* nsICellbroadcastMessenger API
*/
notifyCbMessageReceived: function(aServiceId, aGsmGeographicalScope, aMessageCode,
aMessageId, aLanguage, aBody, aMessageClass,
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup) {
this.messenger.notifyCbMessageReceived(aServiceId, aGsmGeographicalScope, aMessageCode,
aMessageId, aLanguage, aBody, aMessageClass,
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup);
},
/**
* nsIMobileConnectionMessenger API
*/
notifyCdmaInfoRecDisplay: function(aServiceId, aDisplay) {
this.messenger.notifyCdmaInfoRecDisplay(aServiceId, aDisplay);
},
notifyCdmaInfoRecCalledPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.messenger.notifyCdmaInfoRecCalledPartyNumber(aServiceId, aType, aPlan,
aNumber, aPi, aSi);
},
notifyCdmaInfoRecCallingPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.messenger.notifyCdmaInfoRecCallingPartyNumber(aServiceId, aType, aPlan,
aNumber, aPi, aSi);
},
notifyCdmaInfoRecConnectedPartyNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi) {
this.messenger.notifyCdmaInfoRecConnectedPartyNumber(aServiceId, aType, aPlan,
aNumber, aPi, aSi);
},
notifyCdmaInfoRecSignal: function(aServiceId, aType, aAlertPitch, aSignal) {
this.messenger.notifyCdmaInfoRecSignal(aServiceId, aType, aAlertPitch, aSignal);
},
notifyCdmaInfoRecRedirectingNumber: function(aServiceId, aType, aPlan,
aNumber, aPi, aSi, aReason) {
this.messenger.notifyCdmaInfoRecRedirectingNumber(aServiceId, aType, aPlan,
aNumber, aPi, aSi, aReason);
},
notifyCdmaInfoRecLineControl: function(aServiceId, aPolarityIncluded,
aToggle, aReverse, aPowerDenial) {
this.messenger.notifyCdmaInfoRecLineControl(aServiceId, aPolarityIncluded,
aToggle, aReverse, aPowerDenial);
},
notifyCdmaInfoRecClir: function(aServiceId, aCause) {
this.messenger.notifyCdmaInfoRecClir(aServiceId, aCause);
},
notifyCdmaInfoRecAudioControl: function(aServiceId, aUpLink, aDownLink) {
this.messenger.notifyCdmaInfoRecAudioControl(aServiceId, aUpLink, aDownLink);
},
/**
* nsIIccMessenger API
*/
notifyStkProactiveCommand: function(aIccId, aCommand) {
this.messenger.notifyStkProactiveCommand(aIccId, aCommand);
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILSystemMessengerHelper]);

View file

@ -0,0 +1,6 @@
# 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/.
component {19d9a4ea-580d-11e4-8f6c-37ababfaaea9} RILSystemMessengerHelper.js
contract @mozilla.org/ril/system-messenger-helper;1 {19d9a4ea-580d-11e4-8f6c-37ababfaaea9}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
# Copyright 2012 Mozilla Foundation and Mozilla contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# RadioInterfaceLayer.js
component {2d831c8d-6017-435b-a80c-e5d422810cea} RadioInterfaceLayer.js
contract @mozilla.org/ril;1 {2d831c8d-6017-435b-a80c-e5d422810cea}
category profile-after-change RadioInterfaceLayer @mozilla.org/ril;1

View file

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "SystemProperty.h"
#include <dlfcn.h>
#include <string.h>
#include "nsDebug.h"
#include "prinit.h"
namespace mozilla {
namespace system {
namespace {
typedef int (*PropertyGet)(const char*, char*, const char*);
typedef int (*PropertySet)(const char*, const char*);
static void *sLibcUtils;
static PRCallOnceType sInitLibcUtils;
static int
FakePropertyGet(const char* key, char* value, const char* default_value)
{
if(!default_value) {
value[0] = '\0';
return 0;
}
int len = strlen(default_value);
if (len >= Property::VALUE_MAX_LENGTH) {
len = Property::VALUE_MAX_LENGTH - 1;
}
memcpy(value, default_value, len);
value[len] = '\0';
return len;
}
static int
FakePropertySet(const char* key, const char* value)
{
return 0;
}
static PRStatus
InitLibcUtils()
{
sLibcUtils = dlopen("/system/lib/libcutils.so", RTLD_LAZY);
// We will fallback to the fake getter/setter when sLibcUtils is not valid.
return PR_SUCCESS;
}
static void*
GetLibcUtils()
{
PR_CallOnce(&sInitLibcUtils, InitLibcUtils);
return sLibcUtils;
}
} // anonymous namespace
/*static*/ int
Property::Get(const char* key, char* value, const char* default_value)
{
void *libcutils = GetLibcUtils();
if (libcutils) {
PropertyGet getter = (PropertyGet) dlsym(libcutils, "property_get");
if (getter) {
return getter(key, value, default_value);
}
NS_WARNING("Failed to get property_get() from libcutils!");
}
NS_WARNING("Fallback to the FakePropertyGet()");
return FakePropertyGet(key, value, default_value);
}
/*static*/ int
Property::Set(const char* key, const char* value)
{
void *libcutils = GetLibcUtils();
if (libcutils) {
PropertySet setter = (PropertySet) dlsym(libcutils, "property_set");
if (setter) {
return setter(key, value);
}
NS_WARNING("Failed to get property_set() from libcutils!");
}
NS_WARNING("Fallback to the FakePropertySet()");
return FakePropertySet(key, value);
}
} // namespace system
} // namespace mozilla

View file

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_Property_h
#define mozilla_system_Property_h
namespace mozilla {
namespace system {
/**
* Abstraction of property_get/property_get in libcutils from AOSP.
*/
class Property
{
public:
// Constants defined in system_properties.h from AOSP.
enum {
KEY_MAX_LENGTH = 32,
VALUE_MAX_LENGTH = 92
};
static int
Get(const char* key, char* value, const char* default_value);
static int
Set(const char* key, const char* value);
private:
Property() {}
virtual ~Property() {}
};
} // namespace system
} // namespace mozilla
#endif // mozilla_system_Property_h

View file

@ -0,0 +1,214 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SystemWorkerManager.h"
#include "nsINetworkService.h"
#include "nsIWifi.h"
#include "nsIWorkerHolder.h"
#include "nsIXPConnect.h"
#include "jsfriendapi.h"
#include "mozilla/dom/workers/Workers.h"
#include "AutoMounter.h"
#include "TimeZoneSettingObserver.h"
#include "AudioManager.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ipc/KeyStore.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "WifiWorker.h"
#include "mozilla/Services.h"
USING_WORKERS_NAMESPACE
using namespace mozilla::dom::gonk;
using namespace mozilla::ipc;
using namespace mozilla::system;
namespace {
NS_DEFINE_CID(kWifiWorkerCID, NS_WIFIWORKER_CID);
// Doesn't carry a reference, we're owned by services.
SystemWorkerManager *gInstance = nullptr;
} // namespace
SystemWorkerManager::SystemWorkerManager()
: mShutdown(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "There should only be one instance!");
}
SystemWorkerManager::~SystemWorkerManager()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance || gInstance == this,
"There should only be one instance!");
gInstance = nullptr;
}
nsresult
SystemWorkerManager::Init()
{
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ASSERTION(NS_IsMainThread(), "We can only initialize on the main thread");
NS_ASSERTION(!mShutdown, "Already shutdown!");
nsresult rv = InitWifi();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to initialize WiFi Networking!");
return rv;
}
InitKeyStore();
InitAutoMounter();
InitializeTimeZoneSettingObserver();
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
NS_WARNING("Failed to get observer service!");
return NS_ERROR_FAILURE;
}
rv = obs->AddObserver(this, WORKERS_SHUTDOWN_TOPIC, false);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to initialize worker shutdown event!");
return rv;
}
return NS_OK;
}
void
SystemWorkerManager::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mShutdown = true;
ShutdownAutoMounter();
nsCOMPtr<nsIWifi> wifi(do_QueryInterface(mWifiWorker));
if (wifi) {
wifi->Shutdown();
wifi = nullptr;
}
mWifiWorker = nullptr;
if (mKeyStore) {
mKeyStore->Shutdown();
mKeyStore = nullptr;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, WORKERS_SHUTDOWN_TOPIC);
}
}
// static
already_AddRefed<SystemWorkerManager>
SystemWorkerManager::FactoryCreate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
RefPtr<SystemWorkerManager> instance(gInstance);
if (!instance) {
instance = new SystemWorkerManager();
if (NS_FAILED(instance->Init())) {
instance->Shutdown();
return nullptr;
}
gInstance = instance;
}
return instance.forget();
}
// static
nsIInterfaceRequestor*
SystemWorkerManager::GetInterfaceRequestor()
{
return gInstance;
}
NS_IMETHODIMP
SystemWorkerManager::GetInterface(const nsIID &aIID, void **aResult)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aIID.Equals(NS_GET_IID(nsIWifi))) {
return CallQueryInterface(mWifiWorker,
reinterpret_cast<nsIWifi**>(aResult));
}
NS_WARNING("Got nothing for the requested IID!");
return NS_ERROR_NO_INTERFACE;
}
nsresult
SystemWorkerManager::RegisterRilWorker(unsigned int aClientId,
JS::Handle<JS::Value> aWorker,
JSContext *aCx)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
SystemWorkerManager::InitWifi()
{
nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kWifiWorkerCID);
NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE);
mWifiWorker = worker;
return NS_OK;
}
nsresult
SystemWorkerManager::InitKeyStore()
{
mKeyStore = new KeyStore();
return NS_OK;
}
NS_IMPL_ISUPPORTS(SystemWorkerManager,
nsIObserver,
nsIInterfaceRequestor,
nsISystemWorkerManager)
NS_IMETHODIMP
SystemWorkerManager::Observe(nsISupports *aSubject, const char *aTopic,
const char16_t *aData)
{
if (!strcmp(aTopic, WORKERS_SHUTDOWN_TOPIC)) {
Shutdown();
}
return NS_OK;
}

View file

@ -0,0 +1,75 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef mozilla_dom_system_b2g_systemworkermanager_h__
#define mozilla_dom_system_b2g_systemworkermanager_h__
#include "nsIInterfaceRequestor.h"
#include "nsISystemWorkerManager.h"
#include "nsIObserver.h"
#include "nsCOMPtr.h"
#include "nsXULAppAPI.h" // For XRE_GetProcessType
class nsIWorkerHolder;
namespace mozilla {
namespace ipc {
class KeyStore;
}
namespace dom {
namespace gonk {
class SystemWorkerManager final : public nsIObserver,
public nsIInterfaceRequestor,
public nsISystemWorkerManager
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSISYSTEMWORKERMANAGER
nsresult Init();
void Shutdown();
static already_AddRefed<SystemWorkerManager>
FactoryCreate();
static nsIInterfaceRequestor*
GetInterfaceRequestor();
private:
SystemWorkerManager();
~SystemWorkerManager();
nsresult InitWifi();
nsresult InitKeyStore();
nsCOMPtr<nsIWorkerHolder> mWifiWorker;
RefPtr<mozilla::ipc::KeyStore> mKeyStore;
bool mShutdown;
};
}
}
}
#endif // mozilla_dom_system_b2g_systemworkermanager_h__

View file

@ -0,0 +1,891 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
const TETHERINGSERVICE_CONTRACTID = "@mozilla.org/tethering/service;1";
const TETHERINGSERVICE_CID =
Components.ID("{527a4121-ee5a-4651-be9c-f46f59cf7c01}");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
"@mozilla.org/network/service;1",
"nsINetworkService");
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
"nsISettingsService");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
XPCOMUtils.defineLazyGetter(this, "gRil", function() {
try {
return Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
} catch (e) {}
return null;
});
const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
const TOPIC_PREF_CHANGED = "nsPref:changed";
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status";
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
const DEFAULT_USB_INTERFACE_NAME = "rndis0";
const DEFAULT_3G_INTERFACE_NAME = "rmnet0";
const DEFAULT_WIFI_INTERFACE_NAME = "wlan0";
// The kernel's proc entry for network lists.
const KERNEL_NETWORK_ENTRY = "/sys/class/net";
const TETHERING_TYPE_WIFI = "WiFi";
const TETHERING_TYPE_USB = "USB";
const WIFI_FIRMWARE_AP = "AP";
const WIFI_FIRMWARE_STATION = "STA";
const WIFI_SECURITY_TYPE_NONE = "open";
const WIFI_SECURITY_TYPE_WPA_PSK = "wpa-psk";
const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
const WIFI_CTRL_INTERFACE = "wl0.1";
const NETWORK_INTERFACE_UP = "up";
const NETWORK_INTERFACE_DOWN = "down";
const TETHERING_STATE_ONGOING = "ongoing";
const TETHERING_STATE_IDLE = "idle";
const TETHERING_STATE_ACTIVE = "active";
// Settings DB path for USB tethering.
const SETTINGS_USB_ENABLED = "tethering.usb.enabled";
const SETTINGS_USB_IP = "tethering.usb.ip";
const SETTINGS_USB_PREFIX = "tethering.usb.prefix";
const SETTINGS_USB_DHCPSERVER_STARTIP = "tethering.usb.dhcpserver.startip";
const SETTINGS_USB_DHCPSERVER_ENDIP = "tethering.usb.dhcpserver.endip";
const SETTINGS_USB_DNS1 = "tethering.usb.dns1";
const SETTINGS_USB_DNS2 = "tethering.usb.dns2";
// Settings DB path for WIFI tethering.
const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
const SETTINGS_WIFI_DHCPSERVER_ENDIP = "tethering.wifi.dhcpserver.endip";
// Settings DB patch for dun required setting.
const SETTINGS_DUN_REQUIRED = "tethering.dun.required";
// Default value for USB tethering.
const DEFAULT_USB_IP = "192.168.0.1";
const DEFAULT_USB_PREFIX = "24";
const DEFAULT_USB_DHCPSERVER_STARTIP = "192.168.0.10";
const DEFAULT_USB_DHCPSERVER_ENDIP = "192.168.0.30";
const DEFAULT_DNS1 = "8.8.8.8";
const DEFAULT_DNS2 = "8.8.4.4";
const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10";
const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30";
const SETTINGS_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId";
const MOBILE_DUN_CONNECT_TIMEOUT = 30000;
const MOBILE_DUN_RETRY_INTERVAL = 5000;
const MOBILE_DUN_MAX_RETRIES = 5;
var debug;
function updateDebug() {
let debugPref = false; // set default value here.
try {
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
} catch (e) {}
if (debugPref) {
debug = function(s) {
dump("-*- TetheringService: " + s + "\n");
};
} else {
debug = function(s) {};
}
}
updateDebug();
function TetheringService() {
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
Services.obs.addObserver(this, TOPIC_CONNECTION_STATE_CHANGED, false);
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
try {
this._manageOfflineStatus =
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
} catch(ex) {
// Ignore.
}
this._dataDefaultServiceId = 0;
// Possible usb tethering interfaces for different gonk platform.
this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(",");
// Default values for internal and external interfaces.
this._tetheringInterface = {};
this._tetheringInterface[TETHERING_TYPE_USB] = {
externalInterface: DEFAULT_3G_INTERFACE_NAME,
internalInterface: DEFAULT_USB_INTERFACE_NAME
};
this._tetheringInterface[TETHERING_TYPE_WIFI] = {
externalInterface: DEFAULT_3G_INTERFACE_NAME,
internalInterface: DEFAULT_WIFI_INTERFACE_NAME
};
this.tetheringSettings = {};
this.initTetheringSettings();
let settingsLock = gSettingsService.createLock();
// Read the default service id for data call.
settingsLock.get(SETTINGS_DATA_DEFAULT_SERVICE_ID, this);
// Read usb tethering data from settings DB.
settingsLock.get(SETTINGS_USB_IP, this);
settingsLock.get(SETTINGS_USB_PREFIX, this);
settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
settingsLock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
settingsLock.get(SETTINGS_USB_DNS1, this);
settingsLock.get(SETTINGS_USB_DNS2, this);
settingsLock.get(SETTINGS_USB_ENABLED, this);
// Read wifi tethering data from settings DB.
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
this._usbTetheringSettingsToRead = [SETTINGS_USB_IP,
SETTINGS_USB_PREFIX,
SETTINGS_USB_DHCPSERVER_STARTIP,
SETTINGS_USB_DHCPSERVER_ENDIP,
SETTINGS_USB_DNS1,
SETTINGS_USB_DNS2,
SETTINGS_USB_ENABLED,
SETTINGS_WIFI_DHCPSERVER_STARTIP,
SETTINGS_WIFI_DHCPSERVER_ENDIP];
this.wantConnectionEvent = null;
this.dunConnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.dunRetryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._pendingTetheringRequests = [];
}
TetheringService.prototype = {
classID: TETHERINGSERVICE_CID,
classInfo: XPCOMUtils.generateCI({classID: TETHERINGSERVICE_CID,
contractID: TETHERINGSERVICE_CONTRACTID,
classDescription: "Tethering Service",
interfaces: [Ci.nsITetheringService]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsITetheringService,
Ci.nsISupportsWeakReference,
Ci.nsIObserver,
Ci.nsISettingsServiceCallback]),
// Flag to record the default client id for data call.
_dataDefaultServiceId: null,
// Number of usb tehering requests to be processed.
_usbTetheringRequestCount: 0,
// Usb tethering state.
_usbTetheringAction: TETHERING_STATE_IDLE,
// Tethering settings.
tetheringSettings: null,
// Tethering settings need to be read from settings DB.
_usbTetheringSettingsToRead: null,
// Previous usb tethering enabled state.
_oldUsbTetheringEnabledState: null,
// External and internal interface name.
_tetheringInterface: null,
// Dun connection timer.
dunConnectTimer: null,
// Dun connection retry times.
dunRetryTimes: 0,
// Dun retry timer.
dunRetryTimer: null,
// Pending tethering request to handle after dun is connected.
_pendingTetheringRequests: null,
// Flag to indicate wether wifi tethering is being processed.
_wifiTetheringRequestOngoing: false,
// Arguments for pending wifi tethering request.
_pendingWifiTetheringRequestArgs: null,
// The state of tethering.
state: Ci.nsITetheringService.TETHERING_STATE_INACTIVE,
// Flag to check if we can modify the Services.io.offline.
_manageOfflineStatus: true,
// nsIObserver
observe: function(aSubject, aTopic, aData) {
let network;
switch(aTopic) {
case TOPIC_PREF_CHANGED:
if (aData === PREF_NETWORK_DEBUG_ENABLED) {
updateDebug();
}
break;
case TOPIC_MOZSETTINGS_CHANGED:
if ("wrappedJSObject" in aSubject) {
aSubject = aSubject.wrappedJSObject;
}
this.handle(aSubject.key, aSubject.value);
break;
case TOPIC_CONNECTION_STATE_CHANGED:
network = aSubject.QueryInterface(Ci.nsINetworkInfo);
debug("Network " + network.type + "/" + network.name +
" changed state to " + network.state);
this.onConnectionChanged(network);
break;
case TOPIC_XPCOM_SHUTDOWN:
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
Services.obs.removeObserver(this, TOPIC_CONNECTION_STATE_CHANGED);
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
Services.prefs.removeObserver(PREF_MANAGE_OFFLINE_STATUS, this);
this.dunConnectTimer.cancel();
this.dunRetryTimer.cancel();
break;
case PREF_MANAGE_OFFLINE_STATUS:
try {
this._manageOfflineStatus =
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
} catch(ex) {
// Ignore.
}
break;
}
},
// nsISettingsServiceCallback
handle: function(aName, aResult) {
switch(aName) {
case SETTINGS_DATA_DEFAULT_SERVICE_ID:
this._dataDefaultServiceId = aResult || 0;
debug("'_dataDefaultServiceId' is now " + this._dataDefaultServiceId);
break;
case SETTINGS_USB_ENABLED:
this._oldUsbTetheringEnabledState = this.tetheringSettings[SETTINGS_USB_ENABLED];
case SETTINGS_USB_IP:
case SETTINGS_USB_PREFIX:
case SETTINGS_USB_DHCPSERVER_STARTIP:
case SETTINGS_USB_DHCPSERVER_ENDIP:
case SETTINGS_USB_DNS1:
case SETTINGS_USB_DNS2:
case SETTINGS_WIFI_DHCPSERVER_STARTIP:
case SETTINGS_WIFI_DHCPSERVER_ENDIP:
if (aResult !== null) {
this.tetheringSettings[aName] = aResult;
}
debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
let index = this._usbTetheringSettingsToRead.indexOf(aName);
if (index != -1) {
this._usbTetheringSettingsToRead.splice(index, 1);
}
if (this._usbTetheringSettingsToRead.length) {
debug("We haven't read completely the usb Tethering data from settings db.");
break;
}
if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) {
debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do.");
this.handlePendingWifiTetheringRequest();
break;
}
this._usbTetheringRequestCount++;
if (this._usbTetheringRequestCount === 1) {
if (this._wifiTetheringRequestOngoing) {
debug('USB tethering request is blocked by ongoing wifi tethering request.');
} else {
this.handleLastUsbTetheringRequest();
}
}
break;
};
},
handleError: function(aErrorMessage) {
debug("There was an error while reading Tethering settings.");
this.tetheringSettings = {};
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
},
initTetheringSettings: function() {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
this.tetheringSettings[SETTINGS_USB_IP] = DEFAULT_USB_IP;
this.tetheringSettings[SETTINGS_USB_PREFIX] = DEFAULT_USB_PREFIX;
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
this.tetheringSettings[SETTINGS_USB_DNS1] = DEFAULT_DNS1;
this.tetheringSettings[SETTINGS_USB_DNS2] = DEFAULT_DNS2;
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
libcutils.property_get("ro.tethering.dun_required") === "1";
},
getNetworkInfo: function(aType, aServiceId) {
for (let networkId in gNetworkManager.allNetworkInfo) {
let networkInfo = gNetworkManager.allNetworkInfo[networkId];
if (networkInfo.type == aType) {
try {
if (networkInfo instanceof Ci.nsIRilNetworkInfo) {
let rilNetwork = networkInfo.QueryInterface(Ci.nsIRilNetworkInfo);
if (rilNetwork.serviceId != aServiceId) {
continue;
}
}
} catch (e) {}
return networkInfo;
}
}
return null;
},
handleLastUsbTetheringRequest: function() {
debug('handleLastUsbTetheringRequest... ' + this._usbTetheringRequestCount);
if (this._usbTetheringRequestCount === 0) {
if (this.wantConnectionEvent) {
if (this.tetheringSettings[SETTINGS_USB_ENABLED]) {
this.wantConnectionEvent.call(this);
}
this.wantConnectionEvent = null;
}
this.handlePendingWifiTetheringRequest();
return;
}
// Cancel the accumlated count to 1 since we only care about the
// last state.
this._usbTetheringRequestCount = 1;
this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]);
this.wantConnectionEvent = null;
},
handlePendingWifiTetheringRequest: function() {
if (this._pendingWifiTetheringRequestArgs) {
this.setWifiTethering.apply(this, this._pendingWifiTetheringRequestArgs);
this._pendingWifiTetheringRequestArgs = null;
}
},
/**
* Callback when dun connection fails to connect within timeout.
*/
onDunConnectTimerTimeout: function() {
while (this._pendingTetheringRequests.length > 0) {
debug("onDunConnectTimerTimeout: callback without network info.");
let callback = this._pendingTetheringRequests.shift();
if (typeof callback === 'function') {
callback();
}
}
},
setupDunConnection: function() {
this.dunRetryTimer.cancel();
let connection =
gMobileConnectionService.getItemByServiceId(this._dataDefaultServiceId);
let data = connection && connection.data;
if (data && data.state === "registered") {
let ril = gRil.getRadioInterface(this._dataDefaultServiceId);
this.dunRetryTimes = 0;
ril.setupDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
this.dunConnectTimer.cancel();
this.dunConnectTimer.
initWithCallback(this.onDunConnectTimerTimeout.bind(this),
MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
if (this.dunRetryTimes++ >= this.MOBILE_DUN_MAX_RETRIES) {
debug("setupDunConnection: max retries reached.");
this.dunRetryTimes = 0;
// same as dun connect timeout.
this.onDunConnectTimerTimeout();
return;
}
debug("Data not ready, retry dun after " + MOBILE_DUN_RETRY_INTERVAL + " ms.");
this.dunRetryTimer.
initWithCallback(this.setupDunConnection.bind(this),
MOBILE_DUN_RETRY_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
},
_dunActiveUsers: 0,
handleDunConnection: function(aEnable, aCallback) {
debug("handleDunConnection: " + aEnable);
let dun = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN, this._dataDefaultServiceId);
if (!aEnable) {
this._dunActiveUsers--;
if (this._dunActiveUsers > 0) {
debug("Dun still needed by others, do not disconnect.")
return;
}
this.dunRetryTimes = 0;
this.dunRetryTimer.cancel();
this.dunConnectTimer.cancel();
this._pendingTetheringRequests = [];
if (dun && (dun.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
gRil.getRadioInterface(this._dataDefaultServiceId)
.deactivateDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
}
return;
}
this._dunActiveUsers++;
if (!dun || (dun.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
debug("DUN data call inactive, setup dun data call!")
this._pendingTetheringRequests.push(aCallback);
this.dunRetryTimes = 0;
this.setupDunConnection();
return;
}
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name;
aCallback(dun);
},
handleUSBTetheringToggle: function(aEnable) {
debug("handleUSBTetheringToggle: " + aEnable);
if (aEnable &&
(this._usbTetheringAction === TETHERING_STATE_ONGOING ||
this._usbTetheringAction === TETHERING_STATE_ACTIVE)) {
debug("Usb tethering already connecting/connected.");
this._usbTetheringRequestCount = 0;
this.handlePendingWifiTetheringRequest();
return;
}
if (!aEnable &&
this._usbTetheringAction === TETHERING_STATE_IDLE) {
debug("Usb tethering already disconnected.");
this._usbTetheringRequestCount = 0;
this.handlePendingWifiTetheringRequest();
return;
}
if (!aEnable) {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this));
return;
}
this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
this._usbTetheringAction = TETHERING_STATE_ONGOING;
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, (aNetworkInfo) => {
if (!aNetworkInfo){
this.usbTetheringResultReport(aEnable, "Dun connection failed");
return;
}
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
aNetworkInfo.name;
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
});
return;
}
if (gNetworkManager.activeNetworkInfo) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
gNetworkManager.activeNetworkInfo.name;
} else {
let mobile = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
if (mobile && mobile.name) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
}
}
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
},
getUSBTetheringParameters: function(aEnable, aTetheringInterface) {
let interfaceIp = this.tetheringSettings[SETTINGS_USB_IP];
let prefix = this.tetheringSettings[SETTINGS_USB_PREFIX];
let wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
let wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
let usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
let usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
let dns1 = this.tetheringSettings[SETTINGS_USB_DNS1];
let dns2 = this.tetheringSettings[SETTINGS_USB_DNS2];
let internalInterface = aTetheringInterface.internalInterface;
let externalInterface = aTetheringInterface.externalInterface;
// Using the default values here until application support these settings.
if (interfaceIp == "" || prefix == "" ||
wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
usbDhcpStartIp == "" || usbDhcpEndIp == "") {
debug("Invalid subnet information.");
return null;
}
return {
ifname: internalInterface,
ip: interfaceIp,
prefix: prefix,
wifiStartIp: wifiDhcpStartIp,
wifiEndIp: wifiDhcpEndIp,
usbStartIp: usbDhcpStartIp,
usbEndIp: usbDhcpEndIp,
dns1: dns1,
dns2: dns2,
internalIfname: internalInterface,
externalIfname: externalInterface,
enable: aEnable,
link: aEnable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
};
},
notifyError: function(aResetSettings, aCallback, aMsg) {
if (aResetSettings) {
let settingsLock = gSettingsService.createLock();
// Disable wifi tethering with a useful error message for the user.
settingsLock.set("tethering.wifi.enabled", false, null, aMsg);
}
debug("setWifiTethering: " + (aMsg ? aMsg : "success"));
if (aCallback) {
// Callback asynchronously to avoid netsted toggling.
Services.tm.currentThread.dispatch(() => {
aCallback.wifiTetheringEnabledChange(aMsg);
}, Ci.nsIThread.DISPATCH_NORMAL);
}
},
enableWifiTethering: function(aEnable, aConfig, aCallback) {
// Fill in config's required fields.
aConfig.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
aConfig.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
aConfig.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
this._wifiTetheringRequestOngoing = true;
gNetworkService.setWifiTethering(aEnable, aConfig, (aError) => {
// Change the tethering state to WIFI if there is no error.
if (aEnable && !aError) {
this.state = Ci.nsITetheringService.TETHERING_STATE_WIFI;
} else {
// If wifi thethering is disable, or any error happens,
// then consider the following statements.
// Check whether the state is USB now or not. If no then just change
// it to INACTIVE, if yes then just keep it.
// It means that don't let the disable or error of WIFI affect
// the original active state.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_USB) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
// Disconnect dun on error or when wifi tethering is disabled.
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
}
if (this._manageOfflineStatus) {
Services.io.offline = !this.isAnyConnected() &&
(this.state ===
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
}
let resetSettings = aError;
debug('gNetworkService.setWifiTethering finished');
this.notifyError(resetSettings, aCallback, aError);
this._wifiTetheringRequestOngoing = false;
if (this._usbTetheringRequestCount > 0) {
debug('Perform pending USB tethering requests.');
this.handleLastUsbTetheringRequest();
}
});
},
// Enable/disable WiFi tethering by sending commands to netd.
setWifiTethering: function(aEnable, aInterfaceName, aConfig, aCallback) {
debug("setWifiTethering: " + aEnable);
if (!aInterfaceName) {
this.notifyError(true, aCallback, "invalid network interface name");
return;
}
if (!aConfig) {
this.notifyError(true, aCallback, "invalid configuration");
return;
}
if (this._usbTetheringRequestCount > 0) {
// If there's still pending usb tethering request, save
// the request params and redo |setWifiTethering| on
// usb tethering task complete.
debug('USB tethering request is being processed. Queue this wifi tethering request.');
this._pendingWifiTetheringRequestArgs = Array.prototype.slice.call(arguments);
debug('Pending args: ' + JSON.stringify(this._pendingWifiTetheringRequestArgs));
return;
}
// Re-check again, test cases set this property later.
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
libcutils.property_get("ro.tethering.dun_required") === "1";
if (!aEnable) {
this.enableWifiTethering(false, aConfig, aCallback);
return;
}
this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface =
aInterfaceName;
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, (aNetworkInfo) => {
if (!aNetworkInfo) {
this.notifyError(true, aCallback, "Dun connection failed");
return;
}
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface =
aNetworkInfo.name;
this.enableWifiTethering(true, aConfig, aCallback);
});
return;
}
let mobile = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
// Update the real interface name
if (mobile && mobile.name) {
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
}
this.enableWifiTethering(true, aConfig, aCallback);
},
// Enable/disable USB tethering by sending commands to netd.
setUSBTethering: function(aEnable, aTetheringInterface, aCallback) {
let params = this.getUSBTetheringParameters(aEnable, aTetheringInterface);
if (params === null) {
gNetworkService.enableUsbRndis(false, function() {
this.usbTetheringResultReport(aEnable, "Invalid parameters");
});
return;
}
gNetworkService.setUSBTethering(aEnable, params, aCallback);
},
getUsbInterface: function() {
// Find the rndis interface.
for (let i = 0; i < this.possibleInterface.length; i++) {
try {
let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" +
this.possibleInterface[i]);
if (file.exists()) {
return this.possibleInterface[i];
}
} catch (e) {
debug("Not " + this.possibleInterface[i] + " interface.");
}
}
debug("Can't find rndis interface in possible lists.");
return DEFAULT_USB_INTERFACE_NAME;
},
enableUsbRndisResult: function(aSuccess, aEnable) {
if (aSuccess) {
// If enable is false, don't find usb interface cause it is already down,
// just use the internal interface in settings.
if (aEnable) {
this._tetheringInterface[TETHERING_TYPE_USB].internalInterface =
this.getUsbInterface();
}
this.setUSBTethering(aEnable,
this._tetheringInterface[TETHERING_TYPE_USB],
this.usbTetheringResultReport.bind(this, aEnable));
} else {
this.usbTetheringResultReport(aEnable, "enableUsbRndisResult failure");
throw new Error("failed to set USB Function to adb");
}
},
usbTetheringResultReport: function(aEnable, aError) {
this._usbTetheringRequestCount--;
let settingsLock = gSettingsService.createLock();
debug('usbTetheringResultReport callback. enable: ' + aEnable +
', error: ' + aError);
// Disable tethering settings when fail to enable it.
if (aError) {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
settingsLock.set("tethering.usb.enabled", false, null);
// Skip others request when we found an error.
this._usbTetheringRequestCount = 0;
this._usbTetheringAction = TETHERING_STATE_IDLE;
// If the thethering state is WIFI now, then just keep it,
// if not, just change the state to INACTIVE.
// It means that don't let the error of USB affect the original active state.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
} else {
if (aEnable) {
this._usbTetheringAction = TETHERING_STATE_ACTIVE;
this.state = Ci.nsITetheringService.TETHERING_STATE_USB;
} else {
this._usbTetheringAction = TETHERING_STATE_IDLE;
// If the state is now WIFI, don't let the disable of USB affect it.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
}
if (this._manageOfflineStatus) {
Services.io.offline = !this.isAnyConnected() &&
(this.state ===
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
}
this.handleLastUsbTetheringRequest();
}
},
onConnectionChangedReport: function(aSuccess, aExternalIfname) {
debug("onConnectionChangedReport result: success " + aSuccess);
if (aSuccess) {
// Update the external interface.
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
aExternalIfname;
debug("Change the interface name to " + aExternalIfname);
}
},
onConnectionChanged: function(aNetworkInfo) {
if (aNetworkInfo.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
debug("We are only interested in CONNECTED event");
return;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN) {
this.dunConnectTimer.cancel();
debug("DUN data call connected, process callbacks.");
while (this._pendingTetheringRequests.length > 0) {
let callback = this._pendingTetheringRequests.shift();
if (typeof callback === 'function') {
callback(aNetworkInfo);
}
}
return;
}
if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
debug("Usb tethering settings is not enabled");
return;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN &&
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
aNetworkInfo.name) {
debug("Dun required and dun interface is the same");
return;
}
if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
gNetworkManager.activeNetworkInfo.name) {
debug("The active interface is the same");
return;
}
let previous = {
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface
};
let current = {
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
externalIfname: aNetworkInfo.name
};
let callback = (() => {
// Update external network interface.
debug("Update upstream interface to " + aNetworkInfo.name);
gNetworkService.updateUpStream(previous, current,
this.onConnectionChangedReport.bind(this));
});
if (this._usbTetheringAction === TETHERING_STATE_ONGOING) {
debug("Postpone the event and handle it when state is idle.");
this.wantConnectionEvent = callback;
return;
}
this.wantConnectionEvent = null;
callback.call(this);
},
isAnyConnected: function() {
let allNetworkInfo = gNetworkManager.allNetworkInfo;
for (let networkId in allNetworkInfo) {
if (allNetworkInfo.hasOwnProperty(networkId) &&
allNetworkInfo[networkId].state === Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
return true;
}
}
return false;
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TetheringService]);

View file

@ -0,0 +1,4 @@
# TetheringService.js
component {527a4121-ee5a-4651-be9c-f46f59cf7c01} TetheringService.js
contract @mozilla.org/tethering/service;1 {527a4121-ee5a-4651-be9c-f46f59cf7c01}
category profile-after-change TetheringService @mozilla.org/tethering/service;1

View file

@ -0,0 +1,239 @@
/* 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/. */
#include "base/message_loop.h"
#include "jsapi.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/Attributes.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Hal.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsJSUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "TimeZoneSettingObserver.h"
#include "xpcpublic.h"
#include "nsContentUtils.h"
#include "nsPrintfCString.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#undef LOG
#undef ERR
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args)
#define TIME_TIMEZONE "time.timezone"
#define MOZSETTINGS_CHANGED "mozsettings-changed"
using namespace mozilla;
using namespace mozilla::dom;
namespace {
class TimeZoneSettingObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
TimeZoneSettingObserver();
static nsresult SetTimeZone(const JS::Value &aValue, JSContext *aContext);
protected:
virtual ~TimeZoneSettingObserver();
};
class TimeZoneSettingCb final : public nsISettingsServiceCallback
{
public:
NS_DECL_ISUPPORTS
TimeZoneSettingCb() {}
NS_IMETHOD Handle(const nsAString &aName, JS::Handle<JS::Value> aResult) {
JSContext *cx = nsContentUtils::GetCurrentJSContext();
NS_ENSURE_TRUE(cx, NS_OK);
// If we don't have time.timezone value in the settings, we need
// to initialize the settings based on the current system timezone
// to make settings consistent with system. This usually happens
// at the very first boot. After that, settings must have a value.
if (aResult.isNull()) {
// Get the current system time zone offset. Note that we need to
// convert the value to a UTC representation in the format of
// "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret.
// E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30".
int32_t timeZoneOffset = hal::GetTimezoneOffset();
nsPrintfCString curTimeZone("UTC%+03d:%02d",
-timeZoneOffset / 60,
abs(timeZoneOffset) % 60);
// Convert it to a JS string.
NS_ConvertUTF8toUTF16 utf16Str(curTimeZone);
JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
utf16Str.get(),
utf16Str.Length()));
if (!jsStr) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Set the settings based on the current system timezone.
nsCOMPtr<nsISettingsServiceLock> lock;
nsCOMPtr<nsISettingsService> settingsService =
do_GetService("@mozilla.org/settingsService;1");
if (!settingsService) {
ERR("Failed to get settingsLock service!");
return NS_OK;
}
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
JS::Rooted<JS::Value> value(cx, JS::StringValue(jsStr));
lock->Set(TIME_TIMEZONE, value, nullptr, nullptr);
return NS_OK;
}
// Set the system timezone based on the current settings.
if (aResult.isString()) {
return TimeZoneSettingObserver::SetTimeZone(aResult, cx);
}
return NS_OK;
}
NS_IMETHOD HandleError(const nsAString &aName) {
ERR("TimeZoneSettingCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
return NS_OK;
}
protected:
~TimeZoneSettingCb() {}
};
NS_IMPL_ISUPPORTS(TimeZoneSettingCb, nsISettingsServiceCallback)
TimeZoneSettingObserver::TimeZoneSettingObserver()
{
// Setup an observer to watch changes to the setting.
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (!observerService) {
ERR("GetObserverService failed");
return;
}
nsresult rv;
rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
if (NS_FAILED(rv)) {
ERR("AddObserver failed");
return;
}
// Read the 'time.timezone' setting in order to start with a known
// value at boot time. The handle() will be called after reading.
nsCOMPtr<nsISettingsServiceLock> lock;
nsCOMPtr<nsISettingsService> settingsService =
do_GetService("@mozilla.org/settingsService;1");
if (!settingsService) {
ERR("Failed to get settingsLock service!");
return;
}
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
nsCOMPtr<nsISettingsServiceCallback> callback = new TimeZoneSettingCb();
lock->Get(TIME_TIMEZONE, callback);
}
nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
{
// Convert the JS value to a nsCString type.
// The value should be a JS string like "America/Chicago" or "UTC-05:00".
nsAutoJSString valueStr;
if (!valueStr.init(aContext, aValue.toString())) {
ERR("Failed to convert JS value to nsCString");
return NS_ERROR_FAILURE;
}
NS_ConvertUTF16toUTF8 newTimezone(valueStr);
// Hal expects opposite sign from general notations,
// so we need to flip it.
if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) {
if (!newTimezone.SetCharAt('-', 3)) {
return NS_ERROR_FAILURE;
}
} else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) {
if (!newTimezone.SetCharAt('+', 3)) {
return NS_ERROR_FAILURE;
}
}
// Set the timezone only when the system timezone is not identical.
nsCString curTimezone = hal::GetTimezone();
if (!curTimezone.Equals(newTimezone)) {
hal::SetTimezone(newTimezone);
}
return NS_OK;
}
TimeZoneSettingObserver::~TimeZoneSettingObserver()
{
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
}
}
NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver)
NS_IMETHODIMP
TimeZoneSettingObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
return NS_OK;
}
// Note that this function gets called for any and all settings changes,
// so we need to carefully check if we have the one we're interested in.
//
// The string that we're interested in will be a JSON string that looks like:
// {"key":"time.timezone","value":"America/Chicago"} or
// {"key":"time.timezone","value":"UTC-05:00"}
AutoSafeJSContext cx;
RootedDictionary<SettingChangeNotification> setting(cx);
if (!WrappedJSToDictionary(cx, aSubject, setting)) {
return NS_OK;
}
if (!setting.mKey.EqualsASCII(TIME_TIMEZONE)) {
return NS_OK;
}
if (!setting.mValue.isString()) {
return NS_OK;
}
// Set the system timezone.
return SetTimeZone(setting.mValue, cx);
}
} // namespace
static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver;
namespace mozilla {
namespace system {
void
InitializeTimeZoneSettingObserver()
{
sTimeZoneSettingObserver = new TimeZoneSettingObserver();
ClearOnShutdown(&sTimeZoneSettingObserver);
}
} // namespace system
} // namespace mozilla

View file

@ -0,0 +1,20 @@
/* 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/. */
#ifndef mozilla_system_timesetting_h__
#define mozilla_system_timesetting_h__
namespace mozilla {
namespace system {
// Initialize TimeZoneSettingObserver which observes the time zone change
// event from settings service. When receiving the event, it modifies the
// system time zone.
void InitializeTimeZoneSettingObserver();
} // namespace system
} // namespace mozilla
#endif // mozilla_system_timesetting_h__

596
dom/system/gonk/Volume.cpp Normal file
View file

@ -0,0 +1,596 @@
/* 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/. */
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManager.h"
#include "VolumeManagerLog.h"
#include "nsIVolume.h"
#include "nsXULAppAPI.h"
#include <vold/ResponseCode.h>
namespace mozilla {
namespace system {
#if DEBUG_VOLUME_OBSERVER
void
VolumeObserverList::Broadcast(Volume* const& aVolume)
{
uint32_t size = mObservers.Length();
for (uint32_t i = 0; i < size; ++i) {
LOG("VolumeObserverList::Broadcast to [%u] %p volume '%s'",
i, mObservers[i], aVolume->NameStr());
mObservers[i]->Notify(aVolume);
}
}
#endif
VolumeObserverList Volume::sEventObserverList;
// We have a feature where volumes can be locked when mounted. This
// is used to prevent a volume from being shared with the PC while
// it is actively being used (say for storing an update image)
//
// We use WakeLocks (a poor choice of name, but it does what we want)
// from the PowerManagerService to determine when we're locked.
// In particular we'll create a wakelock called volume-NAME-GENERATION
// (where NAME is the volume name, and GENERATION is its generation
// number), and if this wakelock is locked, then we'll prevent a volume
// from being shared.
//
// Implementation Details:
//
// Since the AutoMounter can only control when something gets mounted
// and not when it gets unmounted (for example: a user pulls the SDCard)
// and because Volume and nsVolume data structures are maintained on
// separate threads, we have the potential for some race conditions.
// We eliminate the race conditions by introducing the concept of a
// generation number. Every time a volume transitions to the Mounted
// state, it gets assigned a new generation number. Whenever the state
// of a Volume changes, we send the updated state and current generation
// number to the main thread where it gets updated in the nsVolume.
//
// Since WakeLocks can only be queried from the main-thread, the
// nsVolumeService looks for WakeLock status changes, and forwards
// the results to the IOThread.
//
// If the Volume (IOThread) receives a volume update where the generation
// number mismatches, then the update is simply ignored.
//
// When a Volume (IOThread) initially becomes mounted, we assume it to
// be locked until we get our first update from nsVolume (MainThread).
static int32_t sMountGeneration = 0;
static uint32_t sNextId = 1;
// We don't get media inserted/removed events at startup. So we
// assume it's present, and we'll be told that it's missing.
Volume::Volume(const nsCSubstring& aName)
: mMediaPresent(true),
mState(nsIVolume::STATE_INIT),
mName(aName),
mMountGeneration(-1),
mMountLocked(true), // Needs to agree with nsVolume::nsVolume
mSharingEnabled(false),
mFormatRequested(false),
mMountRequested(false),
mUnmountRequested(false),
mCanBeShared(true),
mIsSharing(false),
mIsFormatting(false),
mIsUnmounting(false),
mIsRemovable(false),
mIsHotSwappable(false),
mId(sNextId++)
{
DBG("Volume %s: created", NameStr());
}
void
Volume::Dump(const char* aLabel) const
{
LOG("%s: Volume: %s (%d) is %s and %s @ %s gen %d locked %d",
aLabel,
NameStr(),
Id(),
StateStr(),
MediaPresent() ? "inserted" : "missing",
MountPoint().get(),
MountGeneration(),
(int)IsMountLocked());
LOG("%s: Sharing %s Mounting %s Formating %s Unmounting %s",
aLabel,
CanBeShared() ? (IsSharingEnabled() ? (IsSharing() ? "en-y" : "en-n")
: "dis")
: "x",
IsMountRequested() ? "req" : "n",
IsFormatRequested() ? (IsFormatting() ? "req-y" : "req-n")
: (IsFormatting() ? "y" : "n"),
IsUnmountRequested() ? (IsUnmounting() ? "req-y" : "req-n")
: (IsUnmounting() ? "y" : "n"));
}
void
Volume::ResolveAndSetMountPoint(const nsCSubstring& aMountPoint)
{
nsCString mountPoint(aMountPoint);
char realPathBuf[PATH_MAX];
// Call realpath so that we wind up with a path which is compatible with
// functions like nsVolumeService::GetVolumeByPath.
if (realpath(mountPoint.get(), realPathBuf) < 0) {
// The path we were handed doesn't exist. Warn about it, but use it
// anyways assuming that the user knows what they're doing.
ERR("ResolveAndSetMountPoint: realpath on '%s' failed: %d",
mountPoint.get(), errno);
mMountPoint = mountPoint;
} else {
mMountPoint = realPathBuf;
}
DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
}
void Volume::SetFakeVolume(const nsACString& aMountPoint)
{
this->mMountLocked = false;
this->mCanBeShared = false;
ResolveAndSetMountPoint(aMountPoint);
SetState(nsIVolume::STATE_MOUNTED);
}
void
Volume::SetIsSharing(bool aIsSharing)
{
if (aIsSharing == mIsSharing) {
return;
}
mIsSharing = aIsSharing;
LOG("Volume %s: IsSharing set to %d state %s",
NameStr(), (int)mIsSharing, StateStr(mState));
sEventObserverList.Broadcast(this);
}
void
Volume::SetIsFormatting(bool aIsFormatting)
{
if (aIsFormatting == mIsFormatting) {
return;
}
mIsFormatting = aIsFormatting;
LOG("Volume %s: IsFormatting set to %d state %s",
NameStr(), (int)mIsFormatting, StateStr(mState));
if (mIsFormatting) {
sEventObserverList.Broadcast(this);
}
}
void
Volume::SetIsUnmounting(bool aIsUnmounting)
{
if (aIsUnmounting == mIsUnmounting) {
return;
}
mIsUnmounting = aIsUnmounting;
LOG("Volume %s: IsUnmounting set to %d state %s",
NameStr(), (int)mIsUnmounting, StateStr(mState));
sEventObserverList.Broadcast(this);
}
void
Volume::SetIsRemovable(bool aIsRemovable)
{
if (aIsRemovable == mIsRemovable) {
return;
}
mIsRemovable = aIsRemovable;
if (!mIsRemovable) {
mIsHotSwappable = false;
}
LOG("Volume %s: IsRemovable set to %d state %s",
NameStr(), (int)mIsRemovable, StateStr(mState));
sEventObserverList.Broadcast(this);
}
void
Volume::SetIsHotSwappable(bool aIsHotSwappable)
{
if (aIsHotSwappable == mIsHotSwappable) {
return;
}
mIsHotSwappable = aIsHotSwappable;
if (mIsHotSwappable) {
mIsRemovable = true;
}
LOG("Volume %s: IsHotSwappable set to %d state %s",
NameStr(), (int)mIsHotSwappable, StateStr(mState));
sEventObserverList.Broadcast(this);
}
bool
Volume::BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue)
{
if (aConfigValue.EqualsLiteral("1") ||
aConfigValue.LowerCaseEqualsLiteral("true")) {
aBoolValue = true;
return true;
}
if (aConfigValue.EqualsLiteral("0") ||
aConfigValue.LowerCaseEqualsLiteral("false")) {
aBoolValue = false;
return true;
}
return false;
}
void
Volume::SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue)
{
if (aConfigName.LowerCaseEqualsLiteral("removable")) {
bool value = false;
if (BoolConfigValue(aConfigValue, value)) {
SetIsRemovable(value);
} else {
ERR("Volume %s: invalid value '%s' for configuration '%s'",
NameStr(), aConfigValue.get(), aConfigName.get());
}
return;
}
if (aConfigName.LowerCaseEqualsLiteral("hotswappable")) {
bool value = false;
if (BoolConfigValue(aConfigValue, value)) {
SetIsHotSwappable(value);
} else {
ERR("Volume %s: invalid value '%s' for configuration '%s'",
NameStr(), aConfigValue.get(), aConfigName.get());
}
return;
}
ERR("Volume %s: invalid config '%s'", NameStr(), aConfigName.get());
}
void
Volume::SetMediaPresent(bool aMediaPresent)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
// mMediaPresent is slightly redunant to the state, however
// when media is removed (while Idle), we get the following:
// 631 Volume sdcard /mnt/sdcard disk removed (179:0)
// 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
//
// And on media insertion, we get:
// 630 Volume sdcard /mnt/sdcard disk inserted (179:0)
// 605 Volume sdcard /mnt/sdcard state changed from 0 (No-Media) to 2 (Pending)
// 605 Volume sdcard /mnt/sdcard state changed from 2 (Pending) to 1 (Idle-Unmounted)
//
// On media removal while the media is mounted:
// 632 Volume sdcard /mnt/sdcard bad removal (179:1)
// 605 Volume sdcard /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)
// 605 Volume sdcard /mnt/sdcard state changed from 5 (Unmounting) to 1 (Idle-Unmounted)
// 631 Volume sdcard /mnt/sdcard disk removed (179:0)
// 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
//
// When sharing with a PC, it goes Mounted -> Idle -> Shared
// When unsharing with a PC, it goes Shared -> Idle -> Mounted
//
// The AutoMounter needs to know whether the media is present or not when
// processing the Idle state.
if (mMediaPresent == aMediaPresent) {
return;
}
LOG("Volume: %s media %s", NameStr(), aMediaPresent ? "inserted" : "removed");
mMediaPresent = aMediaPresent;
sEventObserverList.Broadcast(this);
}
void
Volume::SetSharingEnabled(bool aSharingEnabled)
{
mSharingEnabled = aSharingEnabled;
LOG("SetSharingMode for volume %s to %d canBeShared = %d",
NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
sEventObserverList.Broadcast(this);
}
void
Volume::SetFormatRequested(bool aFormatRequested)
{
mFormatRequested = aFormatRequested;
LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d",
NameStr(), (int)mFormatRequested, (int)CanBeFormatted());
}
void
Volume::SetMountRequested(bool aMountRequested)
{
mMountRequested = aMountRequested;
LOG("SetMountRequested for volume %s to %d CanBeMounted = %d",
NameStr(), (int)mMountRequested, (int)CanBeMounted());
}
void
Volume::SetUnmountRequested(bool aUnmountRequested)
{
mUnmountRequested = aUnmountRequested;
LOG("SetUnmountRequested for volume %s to %d CanBeMounted = %d",
NameStr(), (int)mUnmountRequested, (int)CanBeMounted());
}
void
Volume::SetState(Volume::STATE aNewState)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (aNewState == mState) {
return;
}
if (aNewState == nsIVolume::STATE_MOUNTED) {
mMountGeneration = ++sMountGeneration;
LOG("Volume %s (%u): changing state from %s to %s @ '%s' (%d observers) "
"mountGeneration = %d, locked = %d",
NameStr(), mId, StateStr(mState),
StateStr(aNewState), mMountPoint.get(), sEventObserverList.Length(),
mMountGeneration, (int)mMountLocked);
} else {
LOG("Volume %s (%u): changing state from %s to %s (%d observers)",
NameStr(), mId, StateStr(mState),
StateStr(aNewState), sEventObserverList.Length());
}
switch (aNewState) {
case nsIVolume::STATE_NOMEDIA:
// Cover the startup case where we don't get insertion/removal events
mMediaPresent = false;
mIsSharing = false;
mUnmountRequested = false;
mMountRequested = false;
mIsUnmounting = false;
break;
case nsIVolume::STATE_MOUNTED:
case nsIVolume::STATE_MOUNT_FAIL:
mMountRequested = false;
mIsFormatting = false;
mIsSharing = false;
mIsUnmounting = false;
break;
case nsIVolume::STATE_FORMATTING:
mFormatRequested = false;
mIsFormatting = true;
mIsSharing = false;
mIsUnmounting = false;
break;
case nsIVolume::STATE_SHARED:
case nsIVolume::STATE_SHAREDMNT:
// Covers startup cases. Normally, mIsSharing would be set to true
// when we issue the command to initiate the sharing process, but
// it's conceivable that a volume could already be in a shared state
// when b2g starts.
mIsSharing = true;
mIsUnmounting = false;
mIsFormatting = false;
break;
case nsIVolume::STATE_UNMOUNTING:
mIsUnmounting = true;
mIsFormatting = false;
mIsSharing = false;
break;
case nsIVolume::STATE_IDLE: // Fall through
case nsIVolume::STATE_CHECKMNT: // Fall through
default:
break;
}
mState = aNewState;
sEventObserverList.Broadcast(this);
}
void
Volume::SetMountPoint(const nsCSubstring& aMountPoint)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (mMountPoint.Equals(aMountPoint)) {
return;
}
ResolveAndSetMountPoint(aMountPoint);
}
void
Volume::StartMount(VolumeResponseCallback* aCallback)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
}
void
Volume::StartUnmount(VolumeResponseCallback* aCallback)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
}
void
Volume::StartFormat(VolumeResponseCallback* aCallback)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
StartCommand(new VolumeActionCommand(this, "format", "", aCallback));
}
void
Volume::StartShare(VolumeResponseCallback* aCallback)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
}
void
Volume::StartUnshare(VolumeResponseCallback* aCallback)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
}
void
Volume::StartCommand(VolumeCommand* aCommand)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
VolumeManager::PostCommand(aCommand);
}
//static
void
Volume::RegisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sEventObserverList.AddObserver(aObserver);
DBG("Added Volume Observer '%s' @%p, length = %u",
aName, aObserver, sEventObserverList.Length());
// Send an initial event to the observer (for each volume)
size_t numVolumes = VolumeManager::NumVolumes();
for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
aObserver->Notify(vol);
}
}
//static
void
Volume::UnregisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sEventObserverList.RemoveObserver(aObserver);
DBG("Removed Volume Observer '%s' @%p, length = %u",
aName, aObserver, sEventObserverList.Length());
}
//static
void
Volume::UpdateMountLock(const nsACString& aVolumeName,
const int32_t& aMountGeneration,
const bool& aMountLocked)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
if (!vol || (vol->mMountGeneration != aMountGeneration)) {
return;
}
if (vol->mMountLocked != aMountLocked) {
vol->mMountLocked = aMountLocked;
DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
sEventObserverList.Broadcast(vol);
}
}
void
Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
// The volume name will have already been parsed, and the tokenizer will point
// to the token after the volume name
switch (aResponseCode) {
case ::ResponseCode::VolumeListResult: {
// Each line will look something like:
//
// sdcard /mnt/sdcard 1
//
nsDependentCSubstring mntPoint(aTokenizer.nextToken());
SetMountPoint(mntPoint);
nsresult errCode;
nsCString state(aTokenizer.nextToken());
if (state.EqualsLiteral("X")) {
// Special state for creating fake volumes which can't be shared.
mCanBeShared = false;
SetState(nsIVolume::STATE_MOUNTED);
} else {
SetState((STATE)state.ToInteger(&errCode));
}
break;
}
case ::ResponseCode::VolumeStateChange: {
// Format of the line looks something like:
//
// Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
//
// So we parse out the state after the string " to "
while (aTokenizer.hasMoreTokens()) {
nsAutoCString token(aTokenizer.nextToken());
if (token.EqualsLiteral("to")) {
nsresult errCode;
token = aTokenizer.nextToken();
STATE newState = (STATE)(token.ToInteger(&errCode));
if (newState == nsIVolume::STATE_MOUNTED) {
// We set the state to STATE_CHECKMNT here, and the once the
// AutoMounter detects that the volume is actually accessible
// then the AutoMounter will set the volume as STATE_MOUNTED.
SetState(nsIVolume::STATE_CHECKMNT);
} else {
if (State() == nsIVolume::STATE_CHECKING && newState == nsIVolume::STATE_IDLE) {
LOG("Mount of volume '%s' failed", NameStr());
SetState(nsIVolume::STATE_MOUNT_FAIL);
} else {
SetState(newState);
}
}
break;
}
}
break;
}
case ::ResponseCode::VolumeDiskInserted:
SetMediaPresent(true);
break;
case ::ResponseCode::VolumeDiskRemoved: // fall-thru
case ::ResponseCode::VolumeBadRemoval:
SetMediaPresent(false);
break;
default:
LOG("Volume: %s unrecognized reponse code (ignored)", NameStr());
break;
}
}
} // namespace system
} // namespace mozilla

157
dom/system/gonk/Volume.h Normal file
View file

@ -0,0 +1,157 @@
/* 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/. */
#ifndef mozilla_system_volume_h__
#define mozilla_system_volume_h__
#include "VolumeCommand.h"
#include "nsIVolume.h"
#include "nsString.h"
#include "mozilla/Observer.h"
#include "nsISupportsImpl.h"
#include "nsWhitespaceTokenizer.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* There is an instance of the Volume class for each volume reported
* from vold.
*
* Each volume originates from the /system/etv/vold.fstab file.
*
***************************************************************************/
class Volume;
#define DEBUG_VOLUME_OBSERVER 0
#if DEBUG_VOLUME_OBSERVER
class VolumeObserverList : public mozilla::ObserverList<Volume*>
{
public:
void Broadcast(Volume* const& aVolume);
};
#else
typedef mozilla::ObserverList<Volume*> VolumeObserverList;
#endif
class Volume final
{
public:
NS_INLINE_DECL_REFCOUNTING(Volume)
Volume(const nsCSubstring& aVolumeName);
typedef long STATE; // States are now defined in nsIVolume.idl
static const char* StateStr(STATE aState) { return NS_VolumeStateStr(aState); }
const char* StateStr() const { return StateStr(mState); }
STATE State() const { return mState; }
const nsCString& Name() const { return mName; }
const char* NameStr() const { return mName.get(); }
void Dump(const char* aLabel) const;
// The mount point is the name of the directory where the volume is mounted.
// (i.e. path that leads to the files stored on the volume).
const nsCString& MountPoint() const { return mMountPoint; }
uint32_t Id() const { return mId; }
int32_t MountGeneration() const { return mMountGeneration; }
bool IsMountLocked() const { return mMountLocked; }
bool MediaPresent() const { return mMediaPresent; }
bool CanBeShared() const { return mCanBeShared; }
bool CanBeFormatted() const { return CanBeShared(); }
bool CanBeMounted() const { return CanBeShared(); }
bool IsSharingEnabled() const { return mCanBeShared && mSharingEnabled; }
bool IsFormatRequested() const { return CanBeFormatted() && mFormatRequested; }
bool IsMountRequested() const { return CanBeMounted() && mMountRequested; }
bool IsUnmountRequested() const { return CanBeMounted() && mUnmountRequested; }
bool IsSharing() const { return mIsSharing; }
bool IsFormatting() const { return mIsFormatting; }
bool IsUnmounting() const { return mIsUnmounting; }
bool IsRemovable() const { return mIsRemovable; }
bool IsHotSwappable() const { return mIsHotSwappable; }
void SetFakeVolume(const nsACString& aMountPoint);
void SetSharingEnabled(bool aSharingEnabled);
void SetFormatRequested(bool aFormatRequested);
void SetMountRequested(bool aMountRequested);
void SetUnmountRequested(bool aUnmountRequested);
typedef mozilla::Observer<Volume *> EventObserver;
// NOTE: that observers must live in the IOThread.
static void RegisterVolumeObserver(EventObserver* aObserver, const char* aName);
static void UnregisterVolumeObserver(EventObserver* aObserver, const char* aName);
protected:
~Volume() {}
private:
friend class AutoMounter; // Calls StartXxx
friend class nsVolume; // Calls UpdateMountLock
friend class VolumeManager; // Calls HandleVoldResponse
friend class VolumeListCallback; // Calls SetMountPoint, SetState
// The StartXxx functions will queue up a command to the VolumeManager.
// You can queue up as many commands as you like, and aCallback will
// be called as each one completes.
void StartMount(VolumeResponseCallback* aCallback);
void StartUnmount(VolumeResponseCallback* aCallback);
void StartFormat(VolumeResponseCallback* aCallback);
void StartShare(VolumeResponseCallback* aCallback);
void StartUnshare(VolumeResponseCallback* aCallback);
void SetIsSharing(bool aIsSharing);
void SetIsFormatting(bool aIsFormatting);
void SetIsUnmounting(bool aIsUnmounting);
void SetIsRemovable(bool aIsRemovable);
void SetIsHotSwappable(bool aIsHotSwappable);
void SetState(STATE aNewState);
void SetMediaPresent(bool aMediaPresent);
void SetMountPoint(const nsCSubstring& aMountPoint);
void StartCommand(VolumeCommand* aCommand);
void ResolveAndSetMountPoint(const nsCSubstring& aMountPoint);
bool BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue);
void SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue);
void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer);
static void UpdateMountLock(const nsACString& aVolumeName,
const int32_t& aMountGeneration,
const bool& aMountLocked);
bool mMediaPresent;
STATE mState;
const nsCString mName;
nsCString mMountPoint;
int32_t mMountGeneration;
bool mMountLocked;
bool mSharingEnabled;
bool mFormatRequested;
bool mMountRequested;
bool mUnmountRequested;
bool mCanBeShared;
bool mIsSharing;
bool mIsFormatting;
bool mIsUnmounting;
bool mIsRemovable;
bool mIsHotSwappable;
uint32_t mId; // Unique ID (used by MTP)
static VolumeObserverList sEventObserverList;
};
} // system
} // mozilla
#endif // mozilla_system_volumemanager_h__

View file

@ -0,0 +1,85 @@
/* 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/. */
#include "nsString.h"
#include "nsWhitespaceTokenizer.h"
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManager.h"
#include "VolumeManagerLog.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* The VolumeActionCommand class is used to send commands which apply
* to a particular volume.
*
* The following commands would fit into this category:
*
* volume mount <volname>
* volume unmount <volname> [force]
* volume format <volname>
* volume share <volname> <method>
* volume unshare <volname> <method>
* volume shared <volname> <method>
*
* A typical response looks like:
*
* # vdc volume unshare sdcard ums
* 605 Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
* 200 volume operation succeeded
*
* Note that the 600 series of responses are considered unsolicited and
* are dealt with directly by the VolumeManager. This command will only
* see the terminating response code (200 in the example above).
*
***************************************************************************/
VolumeActionCommand::VolumeActionCommand(Volume* aVolume,
const char* aAction,
const char* aExtraArgs,
VolumeResponseCallback* aCallback)
: VolumeCommand(aCallback),
mVolume(aVolume)
{
nsAutoCString cmd;
cmd = "volume ";
cmd += aAction;
cmd += " ";
cmd += aVolume->Name().get();
// vold doesn't like trailing white space, so only add it if we really need to.
if (aExtraArgs && (*aExtraArgs != '\0')) {
cmd += " ";
cmd += aExtraArgs;
}
SetCmd(cmd);
}
/***************************************************************************
*
* The VolumeListCommand class is used to send the "volume list" command to
* vold.
*
* A typical response looks like:
*
* # vdc volume list
* 110 sdcard /mnt/sdcard 4
* 110 sdcard1 /mnt/sdcard/external_sd 4
* 200 Volumes listed.
*
***************************************************************************/
VolumeListCommand::VolumeListCommand(VolumeResponseCallback* aCallback)
: VolumeCommand(NS_LITERAL_CSTRING("volume list"), aCallback)
{
}
} // system
} // mozilla

View file

@ -0,0 +1,204 @@
/* 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/. */
#ifndef mozilla_system_volumecommand_h__
#define mozilla_system_volumecommand_h__
#include "nsString.h"
#include "nsISupportsImpl.h"
#include "mozilla/RefPtr.h"
#include <algorithm>
#include <vold/ResponseCode.h>
namespace mozilla {
namespace system {
class Volume;
class VolumeCommand;
/***************************************************************************
*
* The VolumeResponseCallback class is an abstract base class. The ResponseReceived
* method will be called for each response received.
*
* Depending on the command, there may be multiple responses for the
* command. Done() will return true if this is the last response.
*
* The responses from vold are all of the form:
*
* <ResponseCode> <String>
*
* Valid Response codes can be found in the vold/ResponseCode.h header.
*
***************************************************************************/
class VolumeResponseCallback
{
protected:
virtual ~VolumeResponseCallback() {}
public:
NS_INLINE_DECL_REFCOUNTING(VolumeResponseCallback)
VolumeResponseCallback()
: mResponseCode(0), mPending(false) {}
bool Done() const
{
// Response codes from the 200, 400, and 500 series all indicated that
// the command has completed.
return (mResponseCode >= ::ResponseCode::CommandOkay)
&& (mResponseCode < ::ResponseCode::UnsolicitedInformational);
}
bool WasSuccessful() const
{
return mResponseCode == ::ResponseCode::CommandOkay;
}
bool IsPending() const { return mPending; }
int ResponseCode() const { return mResponseCode; }
const nsCString &ResponseStr() const { return mResponseStr; }
protected:
virtual void ResponseReceived(const VolumeCommand* aCommand) = 0;
private:
friend class VolumeCommand; // Calls HandleResponse and SetPending
void HandleResponse(const VolumeCommand* aCommand,
int aResponseCode,
nsACString& aResponseStr)
{
mResponseCode = aResponseCode;
#if ANDROID_VERSION >= 17
// There's a sequence number here that we don't care about
// We expect it to be 0. See VolumeCommand::SetCmd
mResponseStr = Substring(aResponseStr, 2);
#else
mResponseStr = aResponseStr;
#endif
if (mResponseCode >= ::ResponseCode::CommandOkay) {
// This is a final response.
mPending = false;
}
ResponseReceived(aCommand);
}
void SetPending(bool aPending) { mPending = aPending; }
int mResponseCode; // The response code parsed from vold
nsCString mResponseStr; // The rest of the line.
bool mPending; // Waiting for response?
};
/***************************************************************************
*
* The VolumeCommand class is an abstract base class used to encapsulate
* volume commands send to vold.
*
* See VolumeManager.h for a list of the volume commands.
*
* Commands sent to vold need an explicit null character so we add one
* to the command to ensure that it's included in the length.
*
* All of these commands are asynchronous in nature, and the
* ResponseReceived callback will be called when a response is available.
*
***************************************************************************/
class VolumeCommand
{
protected:
virtual ~VolumeCommand() {}
public:
NS_INLINE_DECL_REFCOUNTING(VolumeCommand)
VolumeCommand(VolumeResponseCallback* aCallback)
: mBytesConsumed(0),
mCallback(aCallback)
{
SetCmd(NS_LITERAL_CSTRING(""));
}
VolumeCommand(const nsACString& aCommand, VolumeResponseCallback* aCallback)
: mBytesConsumed(0),
mCallback(aCallback)
{
SetCmd(aCommand);
}
void SetCmd(const nsACString& aCommand)
{
mCmd.Truncate();
#if ANDROID_VERSION >= 17
// JB requires a sequence number at the beginning of messages.
// It doesn't matter what we use, so we use 0.
mCmd = "0 ";
#endif
mCmd.Append(aCommand);
// Add a null character. We want this to be included in the length since
// vold uses it to determine the end of the command.
mCmd.Append('\0');
}
const char* CmdStr() const { return mCmd.get(); }
const char* Data() const { return mCmd.Data() + mBytesConsumed; }
size_t BytesConsumed() const { return mBytesConsumed; }
size_t BytesRemaining() const
{
return mCmd.Length() - std::min(mCmd.Length(), mBytesConsumed);
}
void ConsumeBytes(size_t aNumBytes)
{
mBytesConsumed += std::min(BytesRemaining(), aNumBytes);
}
private:
friend class VolumeManager; // Calls SetPending & HandleResponse
void SetPending(bool aPending)
{
if (mCallback) {
mCallback->SetPending(aPending);
}
}
void HandleResponse(int aResponseCode, nsACString& aResponseStr)
{
if (mCallback) {
mCallback->HandleResponse(this, aResponseCode, aResponseStr);
}
}
nsCString mCmd; // Command being sent
size_t mBytesConsumed; // How many bytes have been sent
// Called when a response to the command is received.
RefPtr<VolumeResponseCallback> mCallback;
};
class VolumeActionCommand : public VolumeCommand
{
public:
VolumeActionCommand(Volume* aVolume, const char* aAction,
const char* aExtraArgs, VolumeResponseCallback* aCallback);
private:
RefPtr<Volume> mVolume;
};
class VolumeListCommand : public VolumeCommand
{
public:
VolumeListCommand(VolumeResponseCallback* aCallback);
};
} // system
} // mozilla
#endif // mozilla_system_volumecommand_h__

View file

@ -0,0 +1,591 @@
/* 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/. */
#include "VolumeManager.h"
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManagerLog.h"
#include "VolumeServiceTest.h"
#include "nsWhitespaceTokenizer.h"
#include "nsXULAppAPI.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "mozilla/Scoped.h"
#include "mozilla/StaticPtr.h"
#include <android/log.h>
#include <cutils/sockets.h>
#include <fcntl.h>
#include <sys/socket.h>
namespace mozilla {
namespace system {
static StaticRefPtr<VolumeManager> sVolumeManager;
VolumeManager::STATE VolumeManager::mState = VolumeManager::UNINITIALIZED;
VolumeManager::StateObserverList VolumeManager::mStateObserverList;
/***************************************************************************/
VolumeManager::VolumeManager()
: LineWatcher('\0', kRcvBufSize),
mSocket(-1),
mCommandPending(false)
{
DBG("VolumeManager constructor called");
}
VolumeManager::~VolumeManager()
{
}
//static
void
VolumeManager::Dump(const char* aLabel)
{
if (!sVolumeManager) {
LOG("%s: sVolumeManager == null", aLabel);
return;
}
VolumeArray::size_type numVolumes = NumVolumes();
VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = GetVolume(volIndex);
vol->Dump(aLabel);
}
}
//static
size_t
VolumeManager::NumVolumes()
{
if (!sVolumeManager) {
return 0;
}
return sVolumeManager->mVolumeArray.Length();
}
//static
already_AddRefed<Volume>
VolumeManager::GetVolume(size_t aIndex)
{
MOZ_ASSERT(aIndex < NumVolumes());
RefPtr<Volume> vol = sVolumeManager->mVolumeArray[aIndex];
return vol.forget();
}
//static
VolumeManager::STATE
VolumeManager::State()
{
return mState;
}
//static
const char *
VolumeManager::StateStr(VolumeManager::STATE aState)
{
switch (aState) {
case UNINITIALIZED: return "Uninitialized";
case STARTING: return "Starting";
case VOLUMES_READY: return "Volumes Ready";
}
return "???";
}
//static
void
VolumeManager::SetState(STATE aNewState)
{
if (mState != aNewState) {
LOG("changing state from '%s' to '%s'",
StateStr(mState), StateStr(aNewState));
mState = aNewState;
mStateObserverList.Broadcast(StateChangedEvent());
}
}
//static
void
VolumeManager::RegisterStateObserver(StateObserver* aObserver)
{
mStateObserverList.AddObserver(aObserver);
}
//static
void VolumeManager::UnregisterStateObserver(StateObserver* aObserver)
{
mStateObserverList.RemoveObserver(aObserver);
}
//static
already_AddRefed<Volume>
VolumeManager::FindVolumeByName(const nsCSubstring& aName)
{
if (!sVolumeManager) {
return nullptr;
}
VolumeArray::size_type numVolumes = NumVolumes();
VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = GetVolume(volIndex);
if (vol->Name().Equals(aName)) {
return vol.forget();
}
}
return nullptr;
}
//static
already_AddRefed<Volume>
VolumeManager::FindAddVolumeByName(const nsCSubstring& aName)
{
RefPtr<Volume> vol = FindVolumeByName(aName);
if (vol) {
return vol.forget();
}
// No volume found, create and add a new one.
vol = new Volume(aName);
sVolumeManager->mVolumeArray.AppendElement(vol);
return vol.forget();
}
//static
bool
VolumeManager::RemoveVolumeByName(const nsCSubstring& aName)
{
if (!sVolumeManager) {
return false;
}
VolumeArray::size_type numVolumes = NumVolumes();
VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = GetVolume(volIndex);
if (vol->Name().Equals(aName)) {
sVolumeManager->mVolumeArray.RemoveElementAt(volIndex);
return true;
}
}
// No volume found. Return false to indicate this.
return false;
}
//static
void VolumeManager::InitConfig()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
// This function uses /system/etc/volume.cfg to add additional volumes
// to the Volume Manager.
//
// This is useful on devices like the Nexus 4, which have no physical sd card
// or dedicated partition.
//
// The format of the volume.cfg file is as follows:
// create volume-name mount-point
// configure volume-name preference preference-value
// Blank lines and lines starting with the hash character "#" will be ignored.
ScopedCloseFile fp;
int n = 0;
char line[255];
const char *filename = "/system/etc/volume.cfg";
if (!(fp = fopen(filename, "r"))) {
LOG("Unable to open volume configuration file '%s' - ignoring", filename);
return;
}
while(fgets(line, sizeof(line), fp)) {
n++;
if (line[0] == '#')
continue;
nsCString commandline(line);
nsCWhitespaceTokenizer tokenizer(commandline);
if (!tokenizer.hasMoreTokens()) {
// Blank line - ignore
continue;
}
nsCString command(tokenizer.nextToken());
if (command.EqualsLiteral("create")) {
if (!tokenizer.hasMoreTokens()) {
ERR("No vol_name in %s line %d", filename, n);
continue;
}
nsCString volName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No mount point for volume '%s'. %s line %d",
volName.get(), filename, n);
continue;
}
nsCString mountPoint(tokenizer.nextToken());
RefPtr<Volume> vol = FindAddVolumeByName(volName);
vol->SetFakeVolume(mountPoint);
continue;
}
if (command.EqualsLiteral("configure")) {
if (!tokenizer.hasMoreTokens()) {
ERR("No vol_name in %s line %d", filename, n);
continue;
}
nsCString volName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No configuration name specified for volume '%s'. %s line %d",
volName.get(), filename, n);
continue;
}
nsCString configName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No value for configuration name '%s'. %s line %d",
configName.get(), filename, n);
continue;
}
nsCString configValue(tokenizer.nextToken());
RefPtr<Volume> vol = FindVolumeByName(volName);
if (vol) {
vol->SetConfig(configName, configValue);
} else {
ERR("Invalid volume name '%s'.", volName.get());
}
continue;
}
if (command.EqualsLiteral("ignore")) {
// This command is useful to remove volumes which are being tracked by
// vold, but for which we have no interest.
if (!tokenizer.hasMoreTokens()) {
ERR("No vol_name in %s line %d", filename, n);
continue;
}
nsCString volName(tokenizer.nextToken());
RemoveVolumeByName(volName);
continue;
}
ERR("Unrecognized command: '%s'", command.get());
}
}
void
VolumeManager::DefaultConfig()
{
VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
if (numVolumes == 0) {
return;
}
if (numVolumes == 1) {
// This is to cover early shipping phones like the Buri,
// which had no internal storage, and only external sdcard.
//
// Phones line the nexus-4 which only have an internal
// storage area will need to have a volume.cfg file with
// removable set to false.
RefPtr<Volume> vol = VolumeManager::GetVolume(0);
vol->SetIsRemovable(true);
vol->SetIsHotSwappable(true);
return;
}
VolumeManager::VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
if (!vol->Name().EqualsLiteral("sdcard")) {
vol->SetIsRemovable(true);
vol->SetIsHotSwappable(true);
}
}
}
class VolumeListCallback : public VolumeResponseCallback
{
virtual void ResponseReceived(const VolumeCommand* aCommand)
{
switch (ResponseCode()) {
case ::ResponseCode::VolumeListResult: {
// Each line will look something like:
//
// sdcard /mnt/sdcard 1
//
// So for each volume that we get back, we update any volumes that
// we have of the same name, or add new ones if they don't exist.
nsCWhitespaceTokenizer tokenizer(ResponseStr());
nsDependentCSubstring volName(tokenizer.nextToken());
RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName);
vol->HandleVoldResponse(ResponseCode(), tokenizer);
break;
}
case ::ResponseCode::CommandOkay: {
// We've received the list of volumes. Now read the Volume.cfg
// file to perform customizations, and then tell everybody
// that we're ready for business.
VolumeManager::DefaultConfig();
VolumeManager::InitConfig();
VolumeManager::Dump("READY");
VolumeManager::SetState(VolumeManager::VOLUMES_READY);
break;
}
}
}
};
bool
VolumeManager::OpenSocket()
{
SetState(STARTING);
if ((mSocket.rwget() = socket_local_client("vold",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM)) < 0) {
ERR("Error connecting to vold: (%s) - will retry", strerror(errno));
return false;
}
// add FD_CLOEXEC flag
int flags = fcntl(mSocket.get(), F_GETFD);
if (flags == -1) {
return false;
}
flags |= FD_CLOEXEC;
if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
return false;
}
// set non-blocking
if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
return false;
}
if (!MessageLoopForIO::current()->
WatchFileDescriptor(mSocket.get(),
true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher,
this)) {
return false;
}
LOG("Connected to vold");
PostCommand(new VolumeListCommand(new VolumeListCallback));
return true;
}
//static
void
VolumeManager::PostCommand(VolumeCommand* aCommand)
{
if (!sVolumeManager) {
ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data());
return;
}
aCommand->SetPending(true);
DBG("Sending command '%s'", aCommand->Data());
// vold can only process one command at a time, so add our command
// to the end of the command queue.
sVolumeManager->mCommands.push(aCommand);
if (!sVolumeManager->mCommandPending) {
// There aren't any commands currently being processed, so go
// ahead and kick this one off.
sVolumeManager->mCommandPending = true;
sVolumeManager->WriteCommandData();
}
}
/***************************************************************************
* The WriteCommandData initiates sending of a command to vold. Since
* we're running on the IOThread and not allowed to block, WriteCommandData
* will write as much data as it can, and if not all of the data can be
* written then it will setup a file descriptor watcher and
* OnFileCanWriteWithoutBlocking will call WriteCommandData to write out
* more of the command data.
*/
void
VolumeManager::WriteCommandData()
{
if (mCommands.size() == 0) {
return;
}
VolumeCommand* cmd = mCommands.front();
if (cmd->BytesRemaining() == 0) {
// All bytes have been written. We're waiting for a response.
return;
}
// There are more bytes left to write. Try to write them all.
ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining());
if (bytesWritten < 0) {
ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining());
Restart();
return;
}
DBG("Wrote %d bytes (of %d)", bytesWritten, cmd->BytesRemaining());
cmd->ConsumeBytes(bytesWritten);
if (cmd->BytesRemaining() == 0) {
return;
}
// We were unable to write all of the command bytes. Setup a watcher
// so we'll get called again when we can write without blocking.
if (!MessageLoopForIO::current()->
WatchFileDescriptor(mSocket.get(),
false, // one-shot
MessageLoopForIO::WATCH_WRITE,
&mWriteWatcher,
this)) {
ERR("Failed to setup write watcher for vold socket");
Restart();
}
}
void
VolumeManager::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
{
MOZ_ASSERT(aFd == mSocket.get());
char* endPtr;
int responseCode = strtol(aMessage.Data(), &endPtr, 10);
if (*endPtr == ' ') {
endPtr++;
}
// Now fish out the rest of the line after the response code
nsDependentCString responseLine(endPtr, aMessage.Length() - (endPtr - aMessage.Data()));
DBG("Rcvd: %d '%s'", responseCode, responseLine.Data());
if (responseCode >= ::ResponseCode::UnsolicitedInformational) {
// These are unsolicited broadcasts. We intercept these and process
// them ourselves
HandleBroadcast(responseCode, responseLine);
} else {
// Everything else is considered to be part of the command response.
if (mCommands.size() > 0) {
VolumeCommand* cmd = mCommands.front();
cmd->HandleResponse(responseCode, responseLine);
if (responseCode >= ::ResponseCode::CommandOkay) {
// That's a terminating response. We can remove the command.
mCommands.pop();
mCommandPending = false;
// Start the next command, if there is one.
WriteCommandData();
}
} else {
ERR("Response with no command");
}
}
}
void
VolumeManager::OnFileCanWriteWithoutBlocking(int aFd)
{
MOZ_ASSERT(aFd == mSocket.get());
WriteCommandData();
}
void
VolumeManager::HandleBroadcast(int aResponseCode, nsCString& aResponseLine)
{
// Format of the line is something like:
//
// Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
//
// So we parse out the volume name and the state after the string " to "
nsCWhitespaceTokenizer tokenizer(aResponseLine);
tokenizer.nextToken(); // The word "Volume"
nsDependentCSubstring volName(tokenizer.nextToken());
RefPtr<Volume> vol = FindVolumeByName(volName);
if (!vol) {
return;
}
vol->HandleVoldResponse(aResponseCode, tokenizer);
}
void
VolumeManager::Restart()
{
mReadWatcher.StopWatchingFileDescriptor();
mWriteWatcher.StopWatchingFileDescriptor();
while (!mCommands.empty()) {
mCommands.pop();
}
mCommandPending = false;
mSocket.dispose();
Start();
}
//static
void
VolumeManager::Start()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (!sVolumeManager) {
return;
}
SetState(STARTING);
if (!sVolumeManager->OpenSocket()) {
// Socket open failed, try again in a second.
MessageLoopForIO::current()->
PostDelayedTask(NewRunnableFunction(VolumeManager::Start),
1000);
}
}
void
VolumeManager::OnError()
{
Restart();
}
/***************************************************************************/
static void
InitVolumeManagerIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
MOZ_ASSERT(!sVolumeManager);
sVolumeManager = new VolumeManager();
VolumeManager::Start();
InitVolumeServiceTestIOThread();
}
static void
ShutdownVolumeManagerIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sVolumeManager = nullptr;
}
/**************************************************************************
*
* Public API
*
* Since the VolumeManager runs in IO Thread context, we need to switch
* to IOThread context before we can do anything.
*
**************************************************************************/
void
InitVolumeManager()
{
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(InitVolumeManagerIOThread));
}
void
ShutdownVolumeManager()
{
ShutdownVolumeServiceTest();
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(ShutdownVolumeManagerIOThread));
}
} // system
} // mozilla

View file

@ -0,0 +1,192 @@
/* 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/. */
#ifndef mozilla_system_volumemanager_h__
#define mozilla_system_volumemanager_h__
#include <vector>
#include <queue>
#include "base/message_loop.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Observer.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
#include "Volume.h"
#include "VolumeCommand.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* All of the public API mentioned in this file (unless otherwise
* mentioned) must run from the IOThread.
*
***************************************************************************/
/***************************************************************************
*
* The VolumeManager class is a front-end for android's vold service.
*
* Vold uses a unix socket interface and accepts null-terminated string
* commands. The following commands were determined by examining the vold
* source code:
*
* volume list
* volume mount <volname>
* volume unmount <volname> [force]
* volume debug [on|off]
* volume format <volname>
* volume share <volname> <method>
* volume unshare <volname> <method>
* volume shared <volname> <method>
*
* <volname> is the name of the volume as used in /system/etc/vold.fstab
* <method> is ums
*
* dump
*
* share status <method> (Determines if a particular sharing method is available)
* (GB only - not available in ICS)
*
* storage users (??? always crashes vold ???)
*
* asec list
* asec ...lots more...
*
* obb list
* obb ...lots more...
*
* xwarp enable
* xwarp disable
* xwarp status
*
* There is also a command line tool called vdc, which can be used to send
* the above commands to vold.
*
* Currently, only the volume list, share/unshare, and mount/unmount
* commands are being used.
*
***************************************************************************/
class VolumeManager final : public MessageLoopForIO::LineWatcher
{
virtual ~VolumeManager();
public:
NS_INLINE_DECL_REFCOUNTING(VolumeManager)
typedef nsTArray<RefPtr<Volume>> VolumeArray;
VolumeManager();
//-----------------------------------------------------------------------
//
// State related methods.
//
// The VolumeManager starts off in the STARTING state. Once a connection
// is established with vold, it asks for a list of volumes, and once the
// volume list has been received, then the VolumeManager enters the
// VOLUMES_READY state.
//
// If vold crashes, then the VolumeManager will once again enter the
// STARTING state and try to reestablish a connection with vold.
enum STATE
{
UNINITIALIZED,
STARTING,
VOLUMES_READY
};
static STATE State();
static const char* StateStr(STATE aState);
static const char* StateStr() { return StateStr(State()); }
class StateChangedEvent
{
public:
StateChangedEvent() {}
};
typedef mozilla::Observer<StateChangedEvent> StateObserver;
typedef mozilla::ObserverList<StateChangedEvent> StateObserverList;
static void RegisterStateObserver(StateObserver* aObserver);
static void UnregisterStateObserver(StateObserver* aObserver);
//-----------------------------------------------------------------------
static void Start();
static void Dump(const char* aLabel);
static VolumeArray::size_type NumVolumes();
static already_AddRefed<Volume> GetVolume(VolumeArray::index_type aIndex);
static already_AddRefed<Volume> FindVolumeByName(const nsCSubstring& aName);
static already_AddRefed<Volume> FindAddVolumeByName(const nsCSubstring& aName);
static bool RemoveVolumeByName(const nsCSubstring& aName);
static void InitConfig();
static void PostCommand(VolumeCommand* aCommand);
protected:
virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage);
virtual void OnFileCanWriteWithoutBlocking(int aFd);
virtual void OnError();
static void DefaultConfig();
private:
bool OpenSocket();
friend class VolumeListCallback; // Calls SetState
static void SetState(STATE aNewState);
void Restart();
void WriteCommandData();
void HandleBroadcast(int aResponseCode, nsCString& aResponseLine);
typedef std::queue<RefPtr<VolumeCommand> > CommandQueue;
static STATE mState;
static StateObserverList mStateObserverList;
static const int kRcvBufSize = 1024;
ScopedClose mSocket;
VolumeArray mVolumeArray;
CommandQueue mCommands;
bool mCommandPending;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
RefPtr<VolumeResponseCallback> mBroadcastCallback;
};
/***************************************************************************
*
* The initialization/shutdown functions do not need to be called from
* the IOThread context.
*
***************************************************************************/
/**
* Initialize the Volume Manager. On initialization, the VolumeManager will
* attempt to connect with vold and collect the list of volumes that vold
* knows about.
*/
void InitVolumeManager();
/**
* Shuts down the Volume Manager.
*/
void ShutdownVolumeManager();
} // system
} // mozilla
#endif // mozilla_system_volumemanager_h__

View file

@ -0,0 +1,27 @@
/* 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/. */
#ifndef mozilla_system_volumemanagerlog_h__
#define mozilla_system_volumemanagerlog_h__
#undef USE_DEBUG
#define USE_DEBUG 0
#if !defined(VOLUME_MANAGER_LOG_TAG)
#define VOLUME_MANAGER_LOG_TAG "VolumeManager"
#endif
#undef LOG
#undef ERR
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, VOLUME_MANAGER_LOG_TAG, ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, VOLUME_MANAGER_LOG_TAG, ## args)
#undef DBG
#if USE_DEBUG
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, VOLUME_MANAGER_LOG_TAG, ## args)
#else
#define DBG(args...)
#endif
#endif // mozilla_system_volumemanagerlog_h__

View file

@ -0,0 +1,82 @@
/* 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/. */
#include "VolumeServiceIOThread.h"
#include "base/message_loop.h"
#include "nsVolumeService.h"
#include "nsXULAppAPI.h"
#include "Volume.h"
#include "VolumeManager.h"
namespace mozilla {
namespace system {
VolumeServiceIOThread::VolumeServiceIOThread(nsVolumeService* aVolumeService)
: mVolumeService(aVolumeService)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
VolumeManager::RegisterStateObserver(this);
Volume::RegisterVolumeObserver(this, "VolumeServiceIOThread");
UpdateAllVolumes();
}
VolumeServiceIOThread::~VolumeServiceIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
Volume::UnregisterVolumeObserver(this, "VolumeServiceIOThread");
VolumeManager::UnregisterStateObserver(this);
}
void
VolumeServiceIOThread::Notify(Volume* const & aVolume)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
return;
}
mVolumeService->UpdateVolumeIOThread(aVolume);
}
void
VolumeServiceIOThread::Notify(const VolumeManager::StateChangedEvent& aEvent)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
UpdateAllVolumes();
}
void
VolumeServiceIOThread::UpdateAllVolumes()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
return;
}
VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
VolumeManager::VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
mVolumeService->UpdateVolumeIOThread(vol);
}
}
static StaticRefPtr<VolumeServiceIOThread> sVolumeServiceIOThread;
void
InitVolumeServiceIOThread(nsVolumeService* const & aVolumeService)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sVolumeServiceIOThread = new VolumeServiceIOThread(aVolumeService);
}
void
ShutdownVolumeServiceIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sVolumeServiceIOThread = nullptr;
}
} // system
} // mozilla

View file

@ -0,0 +1,49 @@
/* 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/. */
#ifndef mozilla_system_volumeserviceiothread_h__
#define mozilla_system_volumeserviceiothread_h__
#include "Volume.h"
#include "VolumeManager.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
namespace system {
class nsVolumeService;
/***************************************************************************
* The nsVolumeServiceIOThread is a companion class to the nsVolumeService
* class, but whose methods are called from IOThread.
*/
class VolumeServiceIOThread : public VolumeManager::StateObserver,
public Volume::EventObserver
{
~VolumeServiceIOThread();
public:
NS_INLINE_DECL_REFCOUNTING(VolumeServiceIOThread)
VolumeServiceIOThread(nsVolumeService* aVolumeService);
private:
void UpdateAllVolumes();
virtual void Notify(const VolumeManager::StateChangedEvent& aEvent);
virtual void Notify(Volume* const & aVolume);
RefPtr<nsVolumeService> mVolumeService;
};
void InitVolumeServiceIOThread(nsVolumeService* const & aVolumeService);
void ShutdownVolumeServiceIOThread();
void FormatVolume(const nsCString& aVolume);
void MountVolume(const nsCString& aVolume);
void UnmountVolume(const nsCString& aVolume);
} // system
} // mozilla
#endif // mozilla_system_volumeserviceiothread_h__

View file

@ -0,0 +1,202 @@
/* 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/. */
#include "VolumeServiceTest.h"
#include "base/message_loop.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsIVolume.h"
#include "nsIVolumeService.h"
#include "nsIVolumeStat.h"
#include "nsXULAppAPI.h"
#include "mozilla/Services.h"
#undef VOLUME_MANAGER_LOG_TAG
#define VOLUME_MANAGER_LOG_TAG "VolumeServiceTest"
#include "VolumeManagerLog.h"
using namespace mozilla::services;
namespace mozilla {
namespace system {
#define TEST_NSVOLUME_OBSERVER 0
#if TEST_NSVOLUME_OBSERVER
/***************************************************************************
* A test class to verify that the Observer stuff is working properly.
*/
class VolumeTestObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
VolumeTestObserver()
{
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (!obs) {
return;
}
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
}
~VolumeTestObserver()
{
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (!obs) {
return;
}
obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
}
void LogVolume(nsIVolume* vol)
{
nsString volName;
nsString mountPoint;
int32_t volState;
vol->GetName(volName);
vol->GetMountPoint(mountPoint);
vol->GetState(&volState);
LOG(" Volume: %s MountPoint: %s State: %s",
NS_LossyConvertUTF16toASCII(volName).get(),
NS_LossyConvertUTF16toASCII(mountPoint).get(),
NS_VolumeStateStr(volState));
nsCOMPtr<nsIVolumeStat> stat;
nsresult rv = vol->GetStats(getter_AddRefs(stat));
if (NS_SUCCEEDED(rv)) {
int64_t totalBytes;
int64_t freeBytes;
stat->GetTotalBytes(&totalBytes);
stat->GetFreeBytes(&freeBytes);
LOG(" Total Space: %llu Mb Free Bytes: %llu Mb",
totalBytes / (1024LL * 1024LL), freeBytes / (1024LL * 1024LL));
}
else {
LOG(" Unable to retrieve stats");
}
}
};
static nsCOMPtr<VolumeTestObserver> sTestObserver;
NS_IMPL_ISUPPORTS(VolumeTestObserver, nsIObserver)
NS_IMETHODIMP
VolumeTestObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
LOG("TestObserver: topic: %s", aTopic);
if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
return NS_OK;
}
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
if (vol) {
LogVolume(vol);
}
// Since this observe method was called then we know that the service
// has been initialized so we can do the VolumeService tests.
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
if (!vs) {
ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
return NS_ERROR_FAILURE;
}
nsresult rv = vs->GetVolumeByName(NS_LITERAL_STRING("sdcard"), getter_AddRefs(vol));
if (NS_SUCCEEDED(rv)) {
LOG("GetVolumeByName( 'sdcard' ) succeeded (expected)");
LogVolume(vol);
} else {
ERR("GetVolumeByName( 'sdcard' ) failed (unexpected)");
}
rv = vs->GetVolumeByName(NS_LITERAL_STRING("foo"), getter_AddRefs(vol));
if (NS_SUCCEEDED(rv)) {
ERR("GetVolumeByName( 'foo' ) succeeded (unexpected)");
} else {
LOG("GetVolumeByName( 'foo' ) failed (expected)");
}
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard"), getter_AddRefs(vol));
if (NS_SUCCEEDED(rv)) {
LOG("GetVolumeByPath( '/mnt/sdcard' ) succeeded (expected)");
LogVolume(vol);
} else {
ERR("GetVolumeByPath( '/mnt/sdcard' ) failed (unexpected");
}
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard/foo"), getter_AddRefs(vol));
if (NS_SUCCEEDED(rv)) {
LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) succeeded (expected)");
LogVolume(vol);
} else {
LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) failed (unexpected)");
}
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcardfoo"), getter_AddRefs(vol));
if (NS_SUCCEEDED(rv)) {
ERR("GetVolumeByPath( '/mnt/sdcardfoo' ) succeeded (unexpected)");
} else {
LOG("GetVolumeByPath( '/mnt/sdcardfoo' ) failed (expected)");
}
return NS_OK;
}
class InitVolumeServiceTestIO : public Runnable
{
public:
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
DBG("InitVolumeServiceTest called");
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
if (!vs) {
ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
return NS_ERROR_FAILURE;
}
sTestObserver = new VolumeTestObserver();
return NS_OK;
}
};
#endif // TEST_NSVOLUME_OBSERVER
void
InitVolumeServiceTestIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
#if TEST_NSVOLUME_OBSERVER
// Now that the volume manager is initialized we can go
// ahead and do our test (on main thread).
NS_DispatchToMainThread(new InitVolumeServiceTestIO());
#endif
}
void
ShutdownVolumeServiceTest()
{
#if TEST_NSVOLUME_OBSERVER
DBG("ShutdownVolumeServiceTestIOThread called");
sTestObserver = nullptr;
#endif
}
} // system
} // mozilla

View file

@ -0,0 +1,19 @@
/* 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/. */
#ifndef mozilla_system_volumeservicetest_h__
#define mozilla_system_volumeservicetest_h__
namespace mozilla {
namespace system {
void InitVolumeServiceTestIOThread();
void ShutdownVolumeServiceTest();
} // system
} // mozilla
#endif // mozilla_system_volumeservicetest_h__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,489 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_AUDIOTRACK_H
#define ANDROID_AUDIOTRACK_H
#include <stdint.h>
#include <sys/types.h>
#include "IAudioFlinger.h"
#include "IAudioTrack.h"
#include "AudioSystem.h"
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/IMemory.h>
#include <utils/threads.h>
namespace android {
// ----------------------------------------------------------------------------
class audio_track_cblk_t;
// ----------------------------------------------------------------------------
class AudioTrack
{
public:
enum channel_index {
MONO = 0,
LEFT = 0,
RIGHT = 1
};
/* Events used by AudioTrack callback function (audio_track_cblk_t).
*/
enum event_type {
EVENT_MORE_DATA = 0, // Request to write more data to PCM buffer.
EVENT_UNDERRUN = 1, // PCM buffer underrun occured.
EVENT_LOOP_END = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0.
EVENT_MARKER = 3, // Playback head is at the specified marker position (See setMarkerPosition()).
EVENT_NEW_POS = 4, // Playback head is at a new position (See setPositionUpdatePeriod()).
EVENT_BUFFER_END = 5 // Playback head is at the end of the buffer.
};
/* Create Buffer on the stack and pass it to obtainBuffer()
* and releaseBuffer().
*/
class Buffer
{
public:
enum {
MUTE = 0x00000001
};
uint32_t flags;
int channelCount;
int format;
size_t frameCount;
size_t size;
union {
void* raw;
short* i16;
int8_t* i8;
};
};
/* As a convenience, if a callback is supplied, a handler thread
* is automatically created with the appropriate priority. This thread
* invokes the callback when a new buffer becomes availlable or an underrun condition occurs.
* Parameters:
*
* event: type of event notified (see enum AudioTrack::event_type).
* user: Pointer to context for use by the callback receiver.
* info: Pointer to optional parameter according to event type:
* - EVENT_MORE_DATA: pointer to AudioTrack::Buffer struct. The callback must not write
* more bytes than indicated by 'size' field and update 'size' if less bytes are
* written.
* - EVENT_UNDERRUN: unused.
* - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining.
* - EVENT_MARKER: pointer to an uin32_t containing the marker position in frames.
* - EVENT_NEW_POS: pointer to an uin32_t containing the new position in frames.
* - EVENT_BUFFER_END: unused.
*/
typedef void (*callback_t)(int event, void* user, void *info);
/* Returns the minimum frame count required for the successful creation of
* an AudioTrack object.
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - NO_INIT: audio server or audio hardware not initialized
*/
static status_t getMinFrameCount(int* frameCount,
int streamType =-1,
uint32_t sampleRate = 0);
/* Constructs an uninitialized AudioTrack. No connection with
* AudioFlinger takes place.
*/
AudioTrack();
/* Creates an audio track and registers it with AudioFlinger.
* Once created, the track needs to be started before it can be used.
* Unspecified values are set to the audio hardware's current
* values.
*
* Parameters:
*
* streamType: Select the type of audio stream this track is attached to
* (e.g. AudioSystem::MUSIC).
* sampleRate: Track sampling rate in Hz.
* format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
* 16 bits per sample).
* channels: Channel mask: see AudioSystem::audio_channels.
* frameCount: Total size of track PCM buffer in frames. This defines the
* latency of the track.
* flags: Reserved for future use.
* cbf: Callback function. If not null, this function is called periodically
* to request new PCM data.
* notificationFrames: The callback function is called each time notificationFrames PCM
* frames have been comsumed from track input buffer.
* user Context for use by the callback receiver.
*/
AudioTrack( int streamType,
uint32_t sampleRate = 0,
int format = 0,
int channels = 0,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0,
int sessionId = 0);
/* Creates an audio track and registers it with AudioFlinger. With this constructor,
* The PCM data to be rendered by AudioTrack is passed in a shared memory buffer
* identified by the argument sharedBuffer. This prototype is for static buffer playback.
* PCM data must be present into memory before the AudioTrack is started.
* The Write() and Flush() methods are not supported in this case.
* It is recommented to pass a callback function to be notified of playback end by an
* EVENT_UNDERRUN event.
*/
AudioTrack( int streamType,
uint32_t sampleRate = 0,
int format = 0,
int channels = 0,
const sp<IMemory>& sharedBuffer = 0,
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0,
int sessionId = 0);
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources assotiated with the AudioTrack.
*/
~AudioTrack();
/* Initialize an uninitialized AudioTrack.
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful intialization
* - INVALID_OPERATION: AudioTrack is already intitialized
* - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
* - NO_INIT: audio server or audio hardware not initialized
* */
status_t set(int streamType =-1,
uint32_t sampleRate = 0,
int format = 0,
int channels = 0,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
bool threadCanCallJava = false,
int sessionId = 0);
/* Result of constructing the AudioTrack. This must be checked
* before using any AudioTrack API (except for set()), using
* an uninitialized AudioTrack produces undefined results.
* See set() method above for possible return codes.
*/
status_t initCheck() const;
/* Returns this track's latency in milliseconds.
* This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
* and audio hardware driver.
*/
uint32_t latency() const;
/* getters, see constructor */
int streamType() const;
int format() const;
int channelCount() const;
uint32_t frameCount() const;
int frameSize() const;
sp<IMemory>& sharedBuffer();
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
*/
void start();
/* Stop a track. If set, the callback will cease being called and
* obtainBuffer returns STOPPED. Note that obtainBuffer() still works
* and will fill up buffers until the pool is exhausted.
*/
void stop();
bool stopped() const;
/* flush a stopped track. All pending buffers are discarded.
* This function has no effect if the track is not stoped.
*/
void flush();
/* Pause a track. If set, the callback will cease being called and
* obtainBuffer returns STOPPED. Note that obtainBuffer() still works
* and will fill up buffers until the pool is exhausted.
*/
void pause();
/* mute or unmutes this track.
* While mutted, the callback, if set, is still called.
*/
void mute(bool);
bool muted() const;
/* set volume for this track, mostly used for games' sound effects
* left and right volumes. Levels must be <= 1.0.
*/
status_t setVolume(float left, float right);
void getVolume(float* left, float* right);
/* set the send level for this track. An auxiliary effect should be attached
* to the track with attachEffect(). Level must be <= 1.0.
*/
status_t setAuxEffectSendLevel(float level);
void getAuxEffectSendLevel(float* level);
/* set sample rate for this track, mostly used for games' sound effects
*/
status_t setSampleRate(int sampleRate);
uint32_t getSampleRate();
/* Enables looping and sets the start and end points of looping.
*
* Parameters:
*
* loopStart: loop start expressed as the number of PCM frames played since AudioTrack start.
* loopEnd: loop end expressed as the number of PCM frames played since AudioTrack start.
* loopCount: number of loops to execute. Calling setLoop() with loopCount == 0 cancels any pending or
* active loop. loopCount = -1 means infinite looping.
*
* For proper operation the following condition must be respected:
* (loopEnd-loopStart) <= framecount()
*/
status_t setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount);
status_t getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount);
/* Sets marker position. When playback reaches the number of frames specified, a callback with event
* type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker notification
* callback.
* If the AudioTrack has been opened with no callback function associated, the operation will fail.
*
* Parameters:
*
* marker: marker position expressed in frames.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - INVALID_OPERATION: the AudioTrack has no callback installed.
*/
status_t setMarkerPosition(uint32_t marker);
status_t getMarkerPosition(uint32_t *marker);
/* Sets position update period. Every time the number of frames specified has been played,
* a callback with event type EVENT_NEW_POS is called.
* Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification
* callback.
* If the AudioTrack has been opened with no callback function associated, the operation will fail.
*
* Parameters:
*
* updatePeriod: position update notification period expressed in frames.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - INVALID_OPERATION: the AudioTrack has no callback installed.
*/
status_t setPositionUpdatePeriod(uint32_t updatePeriod);
status_t getPositionUpdatePeriod(uint32_t *updatePeriod);
/* Sets playback head position within AudioTrack buffer. The new position is specified
* in number of frames.
* This method must be called with the AudioTrack in paused or stopped state.
* Note that the actual position set is <position> modulo the AudioTrack buffer size in frames.
* Therefore using this method makes sense only when playing a "static" audio buffer
* as opposed to streaming.
* The getPosition() method on the other hand returns the total number of frames played since
* playback start.
*
* Parameters:
*
* position: New playback head position within AudioTrack buffer.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - INVALID_OPERATION: the AudioTrack is not stopped.
* - BAD_VALUE: The specified position is beyond the number of frames present in AudioTrack buffer
*/
status_t setPosition(uint32_t position);
status_t getPosition(uint32_t *position);
/* Forces AudioTrack buffer full condition. When playing a static buffer, this method avoids
* rewriting the buffer before restarting playback after a stop.
* This method must be called with the AudioTrack in paused or stopped state.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - INVALID_OPERATION: the AudioTrack is not stopped.
*/
status_t reload();
/* returns a handle on the audio output used by this AudioTrack.
*
* Parameters:
* none.
*
* Returned value:
* handle on audio hardware output
*/
audio_io_handle_t getOutput();
/* returns the unique ID associated to this track.
*
* Parameters:
* none.
*
* Returned value:
* AudioTrack ID.
*/
int getSessionId();
/* Attach track auxiliary output to specified effect. Used effectId = 0
* to detach track from effect.
*
* Parameters:
*
* effectId: effectId obtained from AudioEffect::id().
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
* - INVALID_OPERATION: the effect is not an auxiliary effect.
* - BAD_VALUE: The specified effect ID is invalid
*/
status_t attachAuxEffect(int effectId);
/* obtains a buffer of "frameCount" frames. The buffer must be
* filled entirely. If the track is stopped, obtainBuffer() returns
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
* at which point NO_MORE_BUFFERS is returned.
* Buffers will be returned until the pool (buffercount())
* is exhausted, at which point obtainBuffer() will either block
* or return WOULD_BLOCK depending on the value of the "blocking"
* parameter.
*/
enum {
NO_MORE_BUFFERS = 0x80000001,
STOPPED = 1
};
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
void releaseBuffer(Buffer* audioBuffer);
/* As a convenience we provide a write() interface to the audio buffer.
* This is implemented on top of lockBuffer/unlockBuffer. For best
* performance
*
*/
ssize_t write(const void* buffer, size_t size);
/*
* Dumps the state of an audio track.
*/
status_t dump(int fd, const Vector<String16>& args) const;
private:
/* copying audio tracks is not allowed */
AudioTrack(const AudioTrack& other);
AudioTrack& operator = (const AudioTrack& other);
/* a small internal class to handle the callback */
class AudioTrackThread : public Thread
{
public:
AudioTrackThread(AudioTrack& receiver, bool bCanCallJava = false);
private:
friend class AudioTrack;
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
AudioTrack& mReceiver;
Mutex mLock;
};
bool processAudioBuffer(const sp<AudioTrackThread>& thread);
status_t createTrack(int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
bool enforceFrameCount);
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
sp<AudioTrackThread> mAudioTrackThread;
float mVolume[2];
float mSendLevel;
uint32_t mFrameCount;
audio_track_cblk_t* mCblk;
uint8_t mStreamType;
uint8_t mFormat;
uint8_t mChannelCount;
uint8_t mMuted;
uint32_t mChannels;
status_t mStatus;
uint32_t mLatency;
volatile int32_t mActive;
callback_t mCbf;
void* mUserData;
uint32_t mNotificationFramesReq; // requested number of frames between each notification callback
uint32_t mNotificationFramesAct; // actual number of frames between each notification callback
sp<IMemory> mSharedBuffer;
int mLoopCount;
uint32_t mRemainingFrames;
uint32_t mMarkerPosition;
bool mMarkerReached;
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
uint32_t mFlags;
int mSessionId;
int mAuxEffectId;
uint32_t mPadding[8];
};
}; // namespace android
#endif // ANDROID_AUDIOTRACK_H

View file

@ -0,0 +1,798 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_EFFECTAPI_H_
#define ANDROID_EFFECTAPI_H_
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#if __cplusplus
extern "C" {
#endif
/////////////////////////////////////////////////
// Effect control interface
/////////////////////////////////////////////////
// The effect control interface is exposed by each effect engine implementation. It consists of
// a set of functions controlling the configuration, activation and process of the engine.
// The functions are grouped in a structure of type effect_interface_s:
// struct effect_interface_s {
// effect_process_t process;
// effect_command_t command;
// };
// effect_interface_t: Effect control interface handle.
// The effect_interface_t serves two purposes regarding the implementation of the effect engine:
// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
// of the effect control API for a particular effect are located.
// - 2 it is the address of the context of a particular effect instance.
// A typical implementation in the effect library would define a structure as follows:
// struct effect_module_s {
// const struct effect_interface_s *itfe;
// effect_config_t config;
// effect_context_t context;
// }
// The implementation of EffectCreate() function would then allocate a structure of this
// type and return its address as effect_interface_t
typedef struct effect_interface_s **effect_interface_t;
// Effect API version 1.0
#define EFFECT_API_VERSION 0x0100 // Format 0xMMmm MM: Major version, mm: minor version
// Maximum length of character strings in structures defines by this API.
#define EFFECT_STRING_LEN_MAX 64
//
//--- Effect descriptor structure effect_descriptor_t
//
// Unique effect ID (can be generated from the following site:
// http://www.itu.int/ITU-T/asn1/uuid.html)
// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
// - When used for effect type and the engine is implementing and effect corresponding to a standard
// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
// - When used as uuid, it should be a unique UUID for this particular implementation.
typedef struct effect_uuid_s {
uint32_t timeLow;
uint16_t timeMid;
uint16_t timeHiAndVersion;
uint16_t clockSeq;
uint8_t node[6];
} effect_uuid_t;
// NULL UUID definition (matches SL_IID_NULL_)
#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
{ 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
// The effect descriptor contains necessary information to facilitate the enumeration of the effect
// engines present in a library.
typedef struct effect_descriptor_s {
effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect
effect_uuid_t uuid; // UUID for this particular implementation
uint16_t apiVersion; // Version of the effect API implemented: matches EFFECT_API_VERSION
uint32_t flags; // effect engine capabilities/requirements flags (see below)
uint16_t cpuLoad; // CPU load indication (see below)
uint16_t memoryUsage; // Data Memory usage (see below)
char name[EFFECT_STRING_LEN_MAX]; // human readable effect name
char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name
} effect_descriptor_t;
// CPU load and memory usage indication: each effect implementation must provide an indication of
// its CPU and memory usage for the audio effect framework to limit the number of effects
// instantiated at a given time on a given platform.
// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
// The memory usage is expressed in KB and includes only dynamically allocated memory
// Definitions for flags field of effect descriptor.
// +---------------------------+-----------+-----------------------------------
// | description | bits | values
// +---------------------------+-----------+-----------------------------------
// | connection mode | 0..1 | 0 insert: after track process
// | | | 1 auxiliary: connect to track auxiliary
// | | | output and use send level
// | | | 2 replace: replaces track process function;
// | | | must implement SRC, volume and mono to stereo.
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
// | insertion preference | 2..4 | 0 none
// | | | 1 first of the chain
// | | | 2 last of the chain
// | | | 3 exclusive (only effect in the insert chain)
// | | | 4..7 reserved
// +---------------------------+-----------+-----------------------------------
// | Volume management | 5..6 | 0 none
// | | | 1 implements volume control
// | | | 2 requires volume indication
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
// | Device indication | 7..8 | 0 none
// | | | 1 requires device updates
// | | | 2..3 reserved
// +---------------------------+-----------+-----------------------------------
// | Sample input mode | 9..10 | 0 direct: process() function or EFFECT_CMD_CONFIGURE
// | | | command must specify a buffer descriptor
// | | | 1 provider: process() function uses the
// | | | bufferProvider indicated by the
// | | | EFFECT_CMD_CONFIGURE command to request input.
// | | | buffers.
// | | | 2 both: both input modes are supported
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
// | Sample output mode | 11..12 | 0 direct: process() function or EFFECT_CMD_CONFIGURE
// | | | command must specify a buffer descriptor
// | | | 1 provider: process() function uses the
// | | | bufferProvider indicated by the
// | | | EFFECT_CMD_CONFIGURE command to request output
// | | | buffers.
// | | | 2 both: both output modes are supported
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
// | Hardware acceleration | 13..15 | 0 No hardware acceleration
// | | | 1 non tunneled hw acceleration: the process() function
// | | | reads the samples, send them to HW accelerated
// | | | effect processor, reads back the processed samples
// | | | and returns them to the output buffer.
// | | | 2 tunneled hw acceleration: the process() function is
// | | | transparent. The effect interface is only used to
// | | | control the effect engine. This mode is relevant for
// | | | global effects actually applied by the audio
// | | | hardware on the output stream.
// +---------------------------+-----------+-----------------------------------
// | Audio Mode indication | 16..17 | 0 none
// | | | 1 requires audio mode updates
// | | | 2..3 reserved
// +---------------------------+-----------+-----------------------------------
// Insert mode
#define EFFECT_FLAG_TYPE_MASK 0x00000003
#define EFFECT_FLAG_TYPE_INSERT 0x00000000
#define EFFECT_FLAG_TYPE_AUXILIARY 0x00000001
#define EFFECT_FLAG_TYPE_REPLACE 0x00000002
// Insert preference
#define EFFECT_FLAG_INSERT_MASK 0x0000001C
#define EFFECT_FLAG_INSERT_ANY 0x00000000
#define EFFECT_FLAG_INSERT_FIRST 0x00000004
#define EFFECT_FLAG_INSERT_LAST 0x00000008
#define EFFECT_FLAG_INSERT_EXCLUSIVE 0x0000000C
// Volume control
#define EFFECT_FLAG_VOLUME_MASK 0x00000060
#define EFFECT_FLAG_VOLUME_CTRL 0x00000020
#define EFFECT_FLAG_VOLUME_IND 0x00000040
#define EFFECT_FLAG_VOLUME_NONE 0x00000000
// Device indication
#define EFFECT_FLAG_DEVICE_MASK 0x00000180
#define EFFECT_FLAG_DEVICE_IND 0x00000080
#define EFFECT_FLAG_DEVICE_NONE 0x00000000
// Sample input modes
#define EFFECT_FLAG_INPUT_MASK 0x00000600
#define EFFECT_FLAG_INPUT_DIRECT 0x00000000
#define EFFECT_FLAG_INPUT_PROVIDER 0x00000200
#define EFFECT_FLAG_INPUT_BOTH 0x00000400
// Sample output modes
#define EFFECT_FLAG_OUTPUT_MASK 0x00001800
#define EFFECT_FLAG_OUTPUT_DIRECT 0x00000000
#define EFFECT_FLAG_OUTPUT_PROVIDER 0x00000800
#define EFFECT_FLAG_OUTPUT_BOTH 0x00001000
// Hardware acceleration mode
#define EFFECT_FLAG_HW_ACC_MASK 0x00006000
#define EFFECT_FLAG_HW_ACC_SIMPLE 0x00002000
#define EFFECT_FLAG_HW_ACC_TUNNEL 0x00004000
// Audio mode indication
#define EFFECT_FLAG_AUDIO_MODE_MASK 0x00018000
#define EFFECT_FLAG_AUDIO_MODE_IND 0x00008000
#define EFFECT_FLAG_AUDIO_MODE_NONE 0x00000000
// Forward definition of type audio_buffer_t
typedef struct audio_buffer_s audio_buffer_t;
////////////////////////////////////////////////////////////////////////////////
//
// Function: process
//
// Description: Effect process function. Takes input samples as specified
// (count and location) in input buffer descriptor and output processed
// samples as specified in output buffer descriptor. If the buffer descriptor
// is not specified the function must use either the buffer or the
// buffer provider function installed by the EFFECT_CMD_CONFIGURE command.
// The effect framework will call the process() function after the EFFECT_CMD_ENABLE
// command is received and until the EFFECT_CMD_DISABLE is received. When the engine
// receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
// and when done indicate that it is OK to stop calling the process() function by
// returning the -ENODATA status.
//
// NOTE: the process() function implementation should be "real-time safe" that is
// it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
// pthread_cond_wait/pthread_mutex_lock...
//
// Input:
// effect_interface_t: handle to the effect interface this function
// is called on.
// inBuffer: buffer descriptor indicating where to read samples to process.
// If NULL, use the configuration passed by EFFECT_CMD_CONFIGURE command.
//
// inBuffer: buffer descriptor indicating where to write processed samples.
// If NULL, use the configuration passed by EFFECT_CMD_CONFIGURE command.
//
// Output:
// returned value: 0 successful operation
// -ENODATA the engine has finished the disable phase and the framework
// can stop calling process()
// -EINVAL invalid interface handle or
// invalid input/output buffer description
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_process_t)(effect_interface_t self,
audio_buffer_t *inBuffer,
audio_buffer_t *outBuffer);
////////////////////////////////////////////////////////////////////////////////
//
// Function: command
//
// Description: Send a command and receive a response to/from effect engine.
//
// Input:
// effect_interface_t: handle to the effect interface this function
// is called on.
// cmdCode: command code: the command can be a standardized command defined in
// effect_command_e (see below) or a proprietary command.
// cmdSize: size of command in bytes
// pCmdData: pointer to command data
// pReplyData: pointer to reply data
//
// Input/Output:
// replySize: maximum size of reply data as input
// actual size of reply data as output
//
// Output:
// returned value: 0 successful operation
// -EINVAL invalid interface handle or
// invalid command/reply size or format according to command code
// The return code should be restricted to indicate problems related to the this
// API specification. Status related to the execution of a particular command should be
// indicated as part of the reply field.
//
// *pReplyData updated with command response
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_command_t)(effect_interface_t self,
uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t *replySize,
void *pReplyData);
// Effect control interface definition
struct effect_interface_s {
effect_process_t process;
effect_command_t command;
};
//
//--- Standardized command codes for command() function
//
enum effect_command_e {
EFFECT_CMD_INIT, // initialize effect engine
EFFECT_CMD_CONFIGURE, // configure effect engine (see effect_config_t)
EFFECT_CMD_RESET, // reset effect engine
EFFECT_CMD_ENABLE, // enable effect process
EFFECT_CMD_DISABLE, // disable effect process
EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t)
EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred
EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred
EFFECT_CMD_GET_PARAM, // get parameter
EFFECT_CMD_SET_DEVICE, // set audio device (see audio_device_e)
EFFECT_CMD_SET_VOLUME, // set volume
EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...)
EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
};
//==================================================================================================
// command: EFFECT_CMD_INIT
//--------------------------------------------------------------------------------------------------
// description:
// Initialize effect engine: All configurations return to default
//--------------------------------------------------------------------------------------------------
// command format:
// size: 0
// data: N/A
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_CONFIGURE
//--------------------------------------------------------------------------------------------------
// description:
// Apply new audio parameters configurations for input and output buffers
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(effect_config_t)
// data: effect_config_t
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_RESET
//--------------------------------------------------------------------------------------------------
// description:
// Reset the effect engine. Keep configuration but resets state and buffer content
//--------------------------------------------------------------------------------------------------
// command format:
// size: 0
// data: N/A
//--------------------------------------------------------------------------------------------------
// reply format:
// size: 0
// data: N/A
//==================================================================================================
// command: EFFECT_CMD_ENABLE
//--------------------------------------------------------------------------------------------------
// description:
// Enable the process. Called by the framework before the first call to process()
//--------------------------------------------------------------------------------------------------
// command format:
// size: 0
// data: N/A
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_DISABLE
//--------------------------------------------------------------------------------------------------
// description:
// Disable the process. Called by the framework after the last call to process()
//--------------------------------------------------------------------------------------------------
// command format:
// size: 0
// data: N/A
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_SET_PARAM
//--------------------------------------------------------------------------------------------------
// description:
// Set a parameter and apply it immediately
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(effect_param_t) + size of param and value
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_SET_PARAM_DEFERRED
//--------------------------------------------------------------------------------------------------
// description:
// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(effect_param_t) + size of param and value
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
//--------------------------------------------------------------------------------------------------
// reply format:
// size: 0
// data: N/A
//==================================================================================================
// command: EFFECT_CMD_SET_PARAM_COMMIT
//--------------------------------------------------------------------------------------------------
// description:
// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
//--------------------------------------------------------------------------------------------------
// command format:
// size: 0
// data: N/A
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(int)
// data: status
//==================================================================================================
// command: EFFECT_CMD_GET_PARAM
//--------------------------------------------------------------------------------------------------
// description:
// Get a parameter value
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(effect_param_t) + size of param
// data: effect_param_t + param
//--------------------------------------------------------------------------------------------------
// reply format:
// size: sizeof(effect_param_t) + size of param and value
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
//==================================================================================================
// command: EFFECT_CMD_SET_DEVICE
//--------------------------------------------------------------------------------------------------
// description:
// Set the rendering device the audio output path is connected to. See audio_device_e for device
// values.
// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
// command when the device changes
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(uint32_t)
// data: audio_device_e
//--------------------------------------------------------------------------------------------------
// reply format:
// size: 0
// data: N/A
//==================================================================================================
// command: EFFECT_CMD_SET_VOLUME
//--------------------------------------------------------------------------------------------------
// description:
// Set and get volume. Used by audio framework to delegate volume control to effect engine.
// The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
// its descriptor to receive this command before every call to process() function
// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
// the volume that should be applied before the effect is processed. The overall volume (the volume
// actually applied by the effect engine multiplied by the returned value) should match the value
// indicated in the command.
//--------------------------------------------------------------------------------------------------
// command format:
// size: n * sizeof(uint32_t)
// data: volume for each channel defined in effect_config_t for output buffer expressed in
// 8.24 fixed point format
//--------------------------------------------------------------------------------------------------
// reply format:
// size: n * sizeof(uint32_t) / 0
// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
// volume for each channel defined in effect_config_t for output buffer expressed in
// 8.24 fixed point format
// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
// N/A
// It is legal to receive a null pointer as pReplyData in which case the effect framework has
// delegated volume control to another effect
//==================================================================================================
// command: EFFECT_CMD_SET_AUDIO_MODE
//--------------------------------------------------------------------------------------------------
// description:
// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
// descriptor to receive this command when the audio mode changes.
//--------------------------------------------------------------------------------------------------
// command format:
// size: sizeof(uint32_t)
// data: audio_mode_e
//--------------------------------------------------------------------------------------------------
// reply format:
// size: 0
// data: N/A
//==================================================================================================
// command: EFFECT_CMD_FIRST_PROPRIETARY
//--------------------------------------------------------------------------------------------------
// description:
// All proprietary effect commands must use command codes above this value. The size and format of
// command and response fields is free in this case
//==================================================================================================
// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
// regard to the channel mask definition in audio_channels_e e.g :
// Stereo: left, right
// 5 point 1: front left, front right, front center, low frequency, back left, back right
// The buffer size is expressed in frame count, a frame being composed of samples for all
// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
// definition
struct audio_buffer_s {
size_t frameCount; // number of frames in buffer
union {
void* raw; // raw pointer to start of buffer
int32_t* s32; // pointer to signed 32 bit data at start of buffer
int16_t* s16; // pointer to signed 16 bit data at start of buffer
uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer
};
};
// The buffer_provider_s structure contains functions that can be used
// by the effect engine process() function to query and release input
// or output audio buffer.
// The getBuffer() function is called to retrieve a buffer where data
// should read from or written to by process() function.
// The releaseBuffer() function MUST be called when the buffer retrieved
// with getBuffer() is not needed anymore.
// The process function should use the buffer provider mechanism to retrieve
// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_CONFIGURE
// command did not specify an audio buffer.
typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
typedef struct buffer_provider_s {
buffer_function_t getBuffer; // retrieve next buffer
buffer_function_t releaseBuffer; // release used buffer
void *cookie; // for use by client of buffer provider functions
} buffer_provider_t;
// The buffer_config_s structure specifies the input or output audio format
// to be used by the effect engine. It is part of the effect_config_t
// structure that defines both input and output buffer configurations and is
// passed by the EFFECT_CMD_CONFIGURE command.
typedef struct buffer_config_s {
audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
uint32_t samplingRate; // sampling rate
uint32_t channels; // channel mask (see audio_channels_e)
buffer_provider_t bufferProvider; // buffer provider
uint8_t format; // Audio format (see audio_format_e)
uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e)
uint16_t mask; // indicates which of the above fields is valid
} buffer_config_t;
// Sample format
enum audio_format_e {
SAMPLE_FORMAT_PCM_S15, // PCM signed 16 bits
SAMPLE_FORMAT_PCM_U8, // PCM unsigned 8 bits
SAMPLE_FORMAT_PCM_S7_24, // PCM signed 7.24 fixed point representation
SAMPLE_FORMAT_OTHER // other format (e.g. compressed)
};
// Channel mask
enum audio_channels_e {
CHANNEL_FRONT_LEFT = 0x1, // front left channel
CHANNEL_FRONT_RIGHT = 0x2, // front right channel
CHANNEL_FRONT_CENTER = 0x4, // front center channel
CHANNEL_LOW_FREQUENCY = 0x8, // low frequency channel
CHANNEL_BACK_LEFT = 0x10, // back left channel
CHANNEL_BACK_RIGHT = 0x20, // back right channel
CHANNEL_FRONT_LEFT_OF_CENTER = 0x40, // front left of center channel
CHANNEL_FRONT_RIGHT_OF_CENTER = 0x80, // front right of center channel
CHANNEL_BACK_CENTER = 0x100, // back center channel
CHANNEL_MONO = CHANNEL_FRONT_LEFT,
CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
};
// Render device
enum audio_device_e {
DEVICE_EARPIECE = 0x1, // earpiece
DEVICE_SPEAKER = 0x2, // speaker
DEVICE_WIRED_HEADSET = 0x4, // wired headset, with microphone
DEVICE_WIRED_HEADPHONE = 0x8, // wired headphone, without microphone
DEVICE_BLUETOOTH_SCO = 0x10, // generic bluetooth SCO
DEVICE_BLUETOOTH_SCO_HEADSET = 0x20, // bluetooth SCO headset
DEVICE_BLUETOOTH_SCO_CARKIT = 0x40, // bluetooth SCO car kit
DEVICE_BLUETOOTH_A2DP = 0x80, // generic bluetooth A2DP
DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100, // bluetooth A2DP headphones
DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200, // bluetooth A2DP speakers
DEVICE_AUX_DIGITAL = 0x400, // digital output
DEVICE_EXTERNAL_SPEAKER = 0x800 // external speaker (stereo and High quality)
};
#if ANDROID_VERSION < 17
// Audio mode
enum audio_mode_e {
AUDIO_MODE_NORMAL, // device idle
AUDIO_MODE_RINGTONE, // device ringing
AUDIO_MODE_IN_CALL // audio call connected (VoIP or telephony)
};
#endif
// Values for "accessMode" field of buffer_config_t:
// overwrite, read only, accumulate (read/modify/write)
enum effect_buffer_access_e {
EFFECT_BUFFER_ACCESS_WRITE,
EFFECT_BUFFER_ACCESS_READ,
EFFECT_BUFFER_ACCESS_ACCUMULATE
};
// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
// in buffer_config_t must be taken into account when executing the EFFECT_CMD_CONFIGURE command
#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account
#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account
#define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account
#define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account
#define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account
#define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account
#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE
// command to configure audio parameters and buffers for effect engine input and output.
typedef struct effect_config_s {
buffer_config_t inputCfg;
buffer_config_t outputCfg;;
} effect_config_t;
// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
// psize and vsize represent the actual size of parameter and value.
//
// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
//
// +-----------+
// | status | sizeof(int)
// +-----------+
// | psize | sizeof(int)
// +-----------+
// | vsize | sizeof(int)
// +-----------+
// | | | |
// ~ parameter ~ > psize |
// | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int)
// +-----------+ |
// | padding | |
// +-----------+
// | | |
// ~ value ~ > vsize
// | | |
// +-----------+
typedef struct effect_param_s {
int32_t status; // Transaction status (unused for command, used for reply)
uint32_t psize; // Parameter size
uint32_t vsize; // Value size
char data[]; // Start of Parameter + Value data
} effect_param_t;
/////////////////////////////////////////////////
// Effect library interface
/////////////////////////////////////////////////
// An effect library is required to implement and expose the following functions
// to enable effect enumeration and instantiation. The name of these functions must be as
// specified here as the effect framework will get the function address with dlsym():
//
// - effect_QueryNumberEffects_t EffectQueryNumberEffects;
// - effect_QueryEffect_t EffectQueryEffect;
// - effect_CreateEffect_t EffectCreate;
// - effect_ReleaseEffect_t EffectRelease;
////////////////////////////////////////////////////////////////////////////////
//
// Function: EffectQueryNumberEffects
//
// Description: Returns the number of different effects exposed by the
// library. Each effect must have a unique effect uuid (see
// effect_descriptor_t). This function together with EffectQueryEffect()
// is used to enumerate all effects present in the library.
//
// Input/Output:
// pNumEffects: address where the number of effects should be returned.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pNumEffects
// *pNumEffects: updated with number of effects in library
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_QueryNumberEffects_t)(uint32_t *pNumEffects);
////////////////////////////////////////////////////////////////////////////////
//
// Function: EffectQueryEffect
//
// Description: Returns the descriptor of the effect engine which index is
// given as first argument.
// See effect_descriptor_t for details on effect descriptors.
// This function together with EffectQueryNumberEffects() is used to enumerate all
// effects present in the library. The enumeration sequence is:
// EffectQueryNumberEffects(&num_effects);
// for (i = 0; i < num_effects; i++)
// EffectQueryEffect(i,...);
//
// Input/Output:
// index: index of the effect
// pDescriptor: address where to return the effect descriptor.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pDescriptor or index
// -ENOSYS effect list has changed since last execution of
// EffectQueryNumberEffects()
// -ENOENT no more effect available
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_QueryEffect_t)(uint32_t index,
effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
//
// Function: EffectCreate
//
// Description: Creates an effect engine of the specified type and returns an
// effect control interface on this engine. The function will allocate the
// resources for an instance of the requested effect engine and return
// a handle on the effect control interface.
//
// Input:
// uuid: pointer to the effect uuid.
// sessionId: audio session to which this effect instance will be attached. All effects
// created with the same session ID are connected in series and process the same signal
// stream. Knowing that two effects are part of the same effect chain can help the
// library implement some kind of optimizations.
// ioId: identifies the output or input stream this effect is directed to at audio HAL.
// For future use especially with tunneled HW accelerated effects
//
// Input/Output:
// pInterface: address where to return the effect interface.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pEffectUuid or pInterface
// -ENOENT no effect with this uuid found
// *pInterface: updated with the effect interface handle.
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_interface_t *pInterface);
////////////////////////////////////////////////////////////////////////////////
//
// Function: EffectRelease
//
// Description: Releases the effect engine whose handle is given as argument.
// All resources allocated to this particular instance of the effect are
// released.
//
// Input:
// interface: handle on the effect interface to be released.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid interface handle
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_ReleaseEffect_t)(effect_interface_t interface);
#if __cplusplus
} // extern "C"
#endif
#endif /*ANDROID_EFFECTAPI_H_*/

View file

@ -0,0 +1,184 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_IAUDIOFLINGER_H
#define ANDROID_IAUDIOFLINGER_H
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include "IAudioTrack.h"
#include "IAudioRecord.h"
#include "IAudioFlingerClient.h"
#include "EffectApi.h"
#include "IEffect.h"
#include "IEffectClient.h"
#include <utils/String8.h>
namespace android {
// ----------------------------------------------------------------------------
class IAudioFlinger : public IInterface
{
public:
DECLARE_META_INTERFACE(AudioFlinger);
/* create an audio track and registers it with AudioFlinger.
* return null if the track cannot be created.
*/
virtual sp<IAudioTrack> createTrack(
pid_t pid,
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
int *sessionId,
status_t *status) = 0;
virtual sp<IAudioRecord> openRecord(
pid_t pid,
int input,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
int *sessionId,
status_t *status) = 0;
/* query the audio hardware state. This state never changes,
* and therefore can be cached.
*/
virtual uint32_t sampleRate(int output) const = 0;
virtual int channelCount(int output) const = 0;
virtual int format(int output) const = 0;
virtual size_t frameCount(int output) const = 0;
virtual uint32_t latency(int output) const = 0;
/* set/get the audio hardware state. This will probably be used by
* the preference panel, mostly.
*/
virtual status_t setMasterVolume(float value) = 0;
virtual status_t setMasterMute(bool muted) = 0;
virtual float masterVolume() const = 0;
virtual bool masterMute() const = 0;
/* set/get stream type state. This will probably be used by
* the preference panel, mostly.
*/
virtual status_t setStreamVolume(int stream, float value, int output) = 0;
virtual status_t setStreamMute(int stream, bool muted) = 0;
virtual float streamVolume(int stream, int output) const = 0;
virtual bool streamMute(int stream) const = 0;
// set audio mode
virtual status_t setMode(int mode) = 0;
// mic mute/state
virtual status_t setMicMute(bool state) = 0;
virtual bool getMicMute() const = 0;
// is any track active on this stream?
virtual bool isStreamActive(int stream) const = 0;
virtual status_t setParameters(int ioHandle, const String8& keyValuePairs) = 0;
virtual String8 getParameters(int ioHandle, const String8& keys) = 0;
// register a current process for audio output change notifications
virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
// retrieve the audio recording buffer size
virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0;
virtual int openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags) = 0;
virtual int openDuplicateOutput(int output1, int output2) = 0;
virtual status_t closeOutput(int output) = 0;
virtual status_t suspendOutput(int output) = 0;
virtual status_t restoreOutput(int output) = 0;
virtual int openInput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t acoustics) = 0;
virtual status_t closeInput(int input) = 0;
virtual status_t setStreamOutput(uint32_t stream, int output) = 0;
virtual status_t setVoiceVolume(float volume) = 0;
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
virtual unsigned int getInputFramesLost(int ioHandle) = 0;
virtual int newAudioSessionId() = 0;
virtual status_t loadEffectLibrary(const char *libPath, int *handle) = 0;
virtual status_t unloadEffectLibrary(int handle) = 0;
virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
virtual sp<IEffect> createEffect(pid_t pid,
effect_descriptor_t *pDesc,
const sp<IEffectClient>& client,
int32_t priority,
int output,
int sessionId,
status_t *status,
int *id,
int *enabled) = 0;
virtual status_t moveEffects(int session, int srcOutput, int dstOutput) = 0;
};
// ----------------------------------------------------------------------------
class BnAudioFlinger : public BnInterface<IAudioFlinger>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_IAUDIOFLINGER_H

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_IAUDIOFLINGERCLIENT_H
#define ANDROID_IAUDIOFLINGERCLIENT_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <utils/KeyedVector.h>
namespace android {
// ----------------------------------------------------------------------------
class IAudioFlingerClient : public IInterface
{
public:
DECLARE_META_INTERFACE(AudioFlingerClient);
// Notifies a change of audio input/output configuration.
virtual void ioConfigChanged(int event, int ioHandle, void *param2) = 0;
};
// ----------------------------------------------------------------------------
class BnAudioFlingerClient : public BnInterface<IAudioFlingerClient>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_IAUDIOFLINGERCLIENT_H

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IAUDIORECORD_H_
#define IAUDIORECORD_H_
#include <stdint.h>
#include <sys/types.h>
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/IMemory.h>
namespace android {
// ----------------------------------------------------------------------------
class IAudioRecord : public IInterface
{
public:
DECLARE_META_INTERFACE(AudioRecord);
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
*/
virtual status_t start() = 0;
/* Stop a track. If set, the callback will cease being called and
* obtainBuffer will return an error. Buffers that are already released
* will be processed, unless flush() is called.
*/
virtual void stop() = 0;
/* get this tracks control block */
virtual sp<IMemory> getCblk() const = 0;
};
// ----------------------------------------------------------------------------
class BnAudioRecord : public BnInterface<IAudioRecord>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif /*IAUDIORECORD_H_*/

View file

@ -0,0 +1,89 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_IAUDIOTRACK_H
#define ANDROID_IAUDIOTRACK_H
#include <stdint.h>
#include <sys/types.h>
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/IMemory.h>
namespace android {
// ----------------------------------------------------------------------------
class IAudioTrack : public IInterface
{
public:
DECLARE_META_INTERFACE(AudioTrack);
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
*/
virtual status_t start() = 0;
/* Stop a track. If set, the callback will cease being called and
* obtainBuffer will return an error. Buffers that are already released
* will be processed, unless flush() is called.
*/
virtual void stop() = 0;
/* flush a stopped track. All pending buffers are discarded.
* This function has no effect if the track is not stoped.
*/
virtual void flush() = 0;
/* mute or unmutes this track.
* While mutted, the callback, if set, is still called.
*/
virtual void mute(bool) = 0;
/* Pause a track. If set, the callback will cease being called and
* obtainBuffer will return an error. Buffers that are already released
* will be processed, unless flush() is called.
*/
virtual void pause() = 0;
/* Attach track auxiliary output to specified effect. Use effectId = 0
* to detach track from effect.
*/
virtual status_t attachAuxEffect(int effectId) = 0;
/* get this tracks control block */
virtual sp<IMemory> getCblk() const = 0;
};
// ----------------------------------------------------------------------------
class BnAudioTrack : public BnInterface<IAudioTrack>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_IAUDIOTRACK_H

View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_IEFFECT_H
#define ANDROID_IEFFECT_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
namespace android {
class IEffect: public IInterface
{
public:
DECLARE_META_INTERFACE(Effect);
virtual status_t enable() = 0;
virtual status_t disable() = 0;
virtual status_t command(uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t *pReplySize,
void *pReplyData) = 0;
virtual void disconnect() = 0;
virtual sp<IMemory> getCblk() const = 0;
};
// ----------------------------------------------------------------------------
class BnEffect: public BnInterface<IEffect>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
}; // namespace android
#endif // ANDROID_IEFFECT_H

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_IEFFECTCLIENT_H
#define ANDROID_IEFFECTCLIENT_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
namespace android {
class IEffectClient: public IInterface
{
public:
DECLARE_META_INTERFACE(EffectClient);
virtual void controlStatusChanged(bool controlGranted) = 0;
virtual void enableStatusChanged(bool enabled) = 0;
virtual void commandExecuted(uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t replySize,
void *pReplyData) = 0;
};
// ----------------------------------------------------------------------------
class BnEffectClient: public BnInterface<IEffectClient>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
}; // namespace android
#endif // ANDROID_IEFFECTCLIENT_H

107
dom/system/gonk/moz.build Normal file
View file

@ -0,0 +1,107 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# Copyright 2013 Mozilla Foundation and Mozilla contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
XPIDL_SOURCES += [
'nsIAudioManager.idl',
'nsINetworkInterface.idl',
'nsINetworkInterfaceListService.idl',
'nsINetworkManager.idl',
'nsINetworkService.idl',
'nsINetworkWorker.idl',
'nsISystemWorkerManager.idl',
'nsITetheringService.idl',
'nsIVolume.idl',
'nsIVolumeMountLock.idl',
'nsIVolumeService.idl',
'nsIVolumeStat.idl',
'nsIWorkerHolder.idl',
]
XPIDL_MODULE = 'dom_system_gonk'
EXPORTS += [
'GeolocationUtil.h',
'GonkGPSGeolocationProvider.h',
'nsVolume.h',
'nsVolumeService.h',
'SystemProperty.h',
]
UNIFIED_SOURCES += [
'AudioChannelManager.cpp',
'AudioManager.cpp',
'AutoMounter.cpp',
'AutoMounterSetting.cpp',
'GeolocationUtil.cpp',
'GonkGPSGeolocationProvider.cpp',
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'MozMtpStorage.cpp',
'NetIdManager.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',
'nsVolume.cpp',
'nsVolumeMountLock.cpp',
'nsVolumeService.cpp',
'nsVolumeStat.cpp',
'OpenFileFinder.cpp',
'SystemProperty.cpp',
'SystemWorkerManager.cpp',
'TimeZoneSettingObserver.cpp',
'Volume.cpp',
'VolumeCommand.cpp',
'VolumeManager.cpp',
'VolumeServiceIOThread.cpp',
'VolumeServiceTest.cpp',
]
if CONFIG['ANDROID_VERSION'] >= '17':
LOCAL_INCLUDES += ['%' + '%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
else:
LOCAL_INCLUDES += ['%' + '%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
if CONFIG['ENABLE_TESTS']:
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
EXTRA_COMPONENTS += [
'NetworkInterfaceListService.js',
'NetworkInterfaceListService.manifest',
'NetworkManager.js',
'NetworkManager.manifest',
'NetworkService.js',
'NetworkService.manifest',
'TetheringService.js',
'TetheringService.manifest',
]
EXTRA_JS_MODULES += [
'systemlibs.js',
]
include('/ipc/chromium/chromium-config.mozbuild')
DEFINES['HAVE_ANDROID_OS'] = True
LOCAL_INCLUDES += [
'/dom/base',
'/dom/bluetooth/common',
'/dom/geolocation',
'/dom/wifi',
]
FINAL_LIBRARY = 'xul'
FINAL_TARGET_FILES.modules.workers += [
'worker_buf.js',
]

View file

@ -0,0 +1,426 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "MozStumbler.h"
#include "nsDataHashtable.h"
#include "nsGeoPosition.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
#include "StumblerLogging.h"
#include "WriteStumbleOnThread.h"
#include "../GeolocationUtil.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileCellInfo.h"
#include "nsIMobileNetworkInfo.h"
#include "nsINetworkInterface.h"
#include "nsIRadioInterfaceLayer.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
class RequestCellInfoEvent : public Runnable {
public:
RequestCellInfoEvent(StumblerInfo *callback)
: mRequestCallback(callback)
{}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
// Get Cell Info
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n");
return NS_OK;
}
nsCOMPtr<nsIMobileConnection> connection;
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
service->GetNumItems(&numberOfRilServices);
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
if (!connection) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
} else {
cellInfoNum++;
connection->GetCellInfoList(mRequestCallback);
}
}
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
// Get Wifi AP Info
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
if (!wifi) {
mRequestCallback->SetWifiInfoResponseReceived();
STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n");
return NS_OK;
}
wifi->GetWifiScanResults(mRequestCallback);
return NS_OK;
}
private:
RefPtr<StumblerInfo> mRequestCallback;
};
void
MozStumble(nsGeoPosition* position)
{
if (WriteStumbleOnThread::IsFileWaitingForUpload()) {
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
// Knowing that file is waiting to upload, and no collection will take place,
// just trigger the thread with an empty string.
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(EmptyCString());
target->Dispatch(event, NS_DISPATCH_NORMAL);
return;
}
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
position->GetCoords(getter_AddRefs(coords));
if (!coords) {
return;
}
double latitude, longitude;
coords->GetLatitude(&latitude);
coords->GetLongitude(&longitude);
const double kMinChangeInMeters = 30;
static int64_t lastTime_ms = 0;
static double sLastLat = 0;
static double sLastLon = 0;
double delta = -1.0;
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
if (0 != sLastLon || 0 != sLastLat) {
delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon);
}
STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
longitude, latitude, timediff, delta);
// Consecutive GPS locations must be 30 meters and 3 seconds apart
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
sLastLat = latitude;
sLastLon = longitude;
RefPtr<StumblerInfo> requestCallback = new StumblerInfo(position);
RefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
NS_DispatchToMainThread(runnable);
} else {
STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
}
}
void
StumblerInfo::SetWifiInfoResponseReceived()
{
mIsWifiInfoResponseReceived = true;
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetWifiInfoResponseReceived\n");
DumpStumblerInfo();
}
}
void
StumblerInfo::SetCellInfoResponsesExpected(uint8_t count)
{
mCellInfoResponsesExpected = count;
STUMBLER_DBG("SetCellInfoNum (%d)\n", count);
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetCellInfoResponsesExpected\n");
DumpStumblerInfo();
}
}
#define TEXT_LAT NS_LITERAL_CSTRING("latitude")
#define TEXT_LON NS_LITERAL_CSTRING("longitude")
#define TEXT_ACC NS_LITERAL_CSTRING("accuracy")
#define TEXT_ALT NS_LITERAL_CSTRING("altitude")
#define TEXT_ALTACC NS_LITERAL_CSTRING("altitudeAccuracy")
#define TEXT_HEAD NS_LITERAL_CSTRING("heading")
#define TEXT_SPD NS_LITERAL_CSTRING("speed")
nsresult
StumblerInfo::LocationInfoToString(nsACString& aLocDesc)
{
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
mPosition->GetCoords(getter_AddRefs(coords));
if (!coords) {
return NS_ERROR_FAILURE;
}
nsDataHashtable<nsCStringHashKey, double> info;
double val;
coords->GetLatitude(&val);
info.Put(TEXT_LAT, val);
coords->GetLongitude(&val);
info.Put(TEXT_LON, val);
coords->GetAccuracy(&val);
info.Put(TEXT_ACC, val);
coords->GetAltitude(&val);
info.Put(TEXT_ALT, val);
coords->GetAltitudeAccuracy(&val);
info.Put(TEXT_ALTACC, val);
coords->GetHeading(&val);
info.Put(TEXT_HEAD, val);
coords->GetSpeed(&val);
info.Put(TEXT_SPD, val);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
val = it.UserData();
if (!IsNaN(val)) {
aLocDesc += nsPrintfCString("\"%s\":%f,", key.BeginReading(), val);
}
}
aLocDesc += nsPrintfCString("\"timestamp\":%lld,", PR_Now() / PR_USEC_PER_MSEC).get();
return NS_OK;
}
#define TEXT_RADIOTYPE NS_LITERAL_CSTRING("radioType")
#define TEXT_MCC NS_LITERAL_CSTRING("mobileCountryCode")
#define TEXT_MNC NS_LITERAL_CSTRING("mobileNetworkCode")
#define TEXT_LAC NS_LITERAL_CSTRING("locationAreaCode")
#define TEXT_CID NS_LITERAL_CSTRING("cellId")
#define TEXT_PSC NS_LITERAL_CSTRING("psc")
#define TEXT_STRENGTH_ASU NS_LITERAL_CSTRING("asu")
#define TEXT_STRENGTH_DBM NS_LITERAL_CSTRING("signalStrength")
#define TEXT_REGISTERED NS_LITERAL_CSTRING("serving")
#define TEXT_TIMEING_ADVANCE NS_LITERAL_CSTRING("timingAdvance")
template <class T> void
ExtractCommonNonCDMACellInfoItems(nsCOMPtr<T>& cell, nsDataHashtable<nsCStringHashKey, int32_t>& info)
{
int32_t mcc, mnc, cid, sig;
cell->GetMcc(&mcc);
cell->GetMnc(&mnc);
cell->GetCid(&cid);
cell->GetSignalStrength(&sig);
info.Put(TEXT_MCC, mcc);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_CID, cid);
info.Put(TEXT_STRENGTH_ASU, sig);
}
void
StumblerInfo::CellNetworkInfoToString(nsACString& aCellDesc)
{
aCellDesc += "\"cellTowers\": [";
for (uint32_t idx = 0; idx < mCellInfo.Length() ; idx++) {
const char* radioType = 0;
int32_t type;
mCellInfo[idx]->GetType(&type);
bool registered;
mCellInfo[idx]->GetRegistered(&registered);
if (idx) {
aCellDesc += ",{";
} else {
aCellDesc += "{";
}
STUMBLER_DBG("type=%d\n", type);
nsDataHashtable<nsCStringHashKey, int32_t> info;
info.Put(TEXT_REGISTERED, registered);
if(type == nsICellInfo::CELL_INFO_TYPE_GSM) {
radioType = "gsm";
nsCOMPtr<nsIGsmCellInfo> gsmCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(gsmCellInfo, info);
int32_t lac;
gsmCellInfo->GetLac(&lac);
info.Put(TEXT_LAC, lac);
} else if (type == nsICellInfo::CELL_INFO_TYPE_WCDMA) {
radioType = "wcdma";
nsCOMPtr<nsIWcdmaCellInfo> wcdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(wcdmaCellInfo, info);
int32_t lac, psc;
wcdmaCellInfo->GetLac(&lac);
wcdmaCellInfo->GetPsc(&psc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_PSC, psc);
} else if (type == nsICellInfo::CELL_INFO_TYPE_CDMA) {
radioType = "cdma";
nsCOMPtr<nsICdmaCellInfo> cdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
int32_t mnc, lac, cid, sig;
cdmaCellInfo->GetSystemId(&mnc);
cdmaCellInfo->GetNetworkId(&lac);
cdmaCellInfo->GetBaseStationId(&cid);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_CID, cid);
cdmaCellInfo->GetEvdoDbm(&sig);
if (sig < 0 || sig == nsICellInfo::UNKNOWN_VALUE) {
cdmaCellInfo->GetCdmaDbm(&sig);
}
if (sig > -1 && sig != nsICellInfo::UNKNOWN_VALUE) {
sig *= -1;
info.Put(TEXT_STRENGTH_DBM, sig);
}
} else if (type == nsICellInfo::CELL_INFO_TYPE_LTE) {
radioType = "lte";
nsCOMPtr<nsILteCellInfo> lteCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(lteCellInfo, info);
int32_t lac, timingAdvance, pcid, rsrp;
lteCellInfo->GetTac(&lac);
lteCellInfo->GetTimingAdvance(&timingAdvance);
lteCellInfo->GetPcid(&pcid);
lteCellInfo->GetRsrp(&rsrp);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_TIMEING_ADVANCE, timingAdvance);
info.Put(TEXT_PSC, pcid);
if (rsrp != nsICellInfo::UNKNOWN_VALUE) {
info.Put(TEXT_STRENGTH_DBM, rsrp * -1);
}
}
aCellDesc += nsPrintfCString("\"%s\":\"%s\"", TEXT_RADIOTYPE.get(), radioType);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
int32_t value = it.UserData();
if (value != nsICellInfo::UNKNOWN_VALUE) {
aCellDesc += nsPrintfCString(",\"%s\":%d", key.BeginReading(), value);
}
}
aCellDesc += "}";
}
aCellDesc += "]";
}
void
StumblerInfo::DumpStumblerInfo()
{
if (!mIsWifiInfoResponseReceived || mCellInfoResponsesReceived != mCellInfoResponsesExpected) {
STUMBLER_DBG("CellInfoReceived=%d (Expected=%d), WifiInfoResponseReceived=%d\n",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
return;
}
mIsWifiInfoResponseReceived = false;
mCellInfoResponsesReceived = 0;
nsAutoCString desc;
nsresult rv = LocationInfoToString(desc);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("LocationInfoToString failed, skip this dump");
return;
}
CellNetworkInfoToString(desc);
desc += mWifiDesc;
STUMBLER_DBG("dispatch write event to thread\n");
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(desc);
target->Dispatch(event, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d cellinfo in the result\n", count);
for (uint32_t i = 0; i < count; i++) {
mCellInfo.AppendElement(aCellInfos[i]);
}
mCellInfoResponsesReceived++;
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP StumblerInfo::NotifyGetCellInfoListFailed(const nsAString& error)
{
MOZ_ASSERT(NS_IsMainThread());
mCellInfoResponsesReceived++;
STUMBLER_ERR("NotifyGetCellInfoListFailedm CellInfoReadyNum=%d, mCellInfoResponsesExpected=%d, mIsWifiInfoResponseReceived=%d",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onready(uint32_t count, nsIWifiScanResult** results)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d wifiAPinfo in the result\n",count);
mWifiDesc += ",\"wifiAccessPoints\": [";
bool firstItem = true;
for (uint32_t i = 0 ; i < count ; i++) {
nsString ssid;
results[i]->GetSsid(ssid);
if (ssid.IsEmpty()) {
STUMBLER_DBG("no ssid, skip this AP\n");
continue;
}
if (ssid.Length() >= 6) {
if (StringEndsWith(ssid, NS_LITERAL_STRING("_nomap"))) {
STUMBLER_DBG("end with _nomap. skip this AP(ssid :%s)\n", ssid.get());
continue;
}
}
if (firstItem) {
mWifiDesc += "{";
firstItem = false;
} else {
mWifiDesc += ",{";
}
// mac address
nsString bssid;
results[i]->GetBssid(bssid);
// 00:00:00:00:00:00 --> 000000000000
bssid.StripChars(":");
mWifiDesc += "\"macAddress\":\"";
mWifiDesc += NS_ConvertUTF16toUTF8(bssid);
uint32_t signal;
results[i]->GetSignalStrength(&signal);
mWifiDesc += "\",\"signalStrength\":";
mWifiDesc.AppendInt(signal);
mWifiDesc += "}";
}
mWifiDesc += "]";
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onfailure()
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_ERR("GetWifiScanResults Onfailure\n");
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}

View file

@ -0,0 +1,47 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_system_mozstumbler_h__
#define mozilla_system_mozstumbler_h__
#include "nsIDOMEventTarget.h"
#include "nsICellInfo.h"
#include "nsIWifi.h"
#define STUMBLE_INTERVAL_MS 3000
class nsGeoPosition;
void MozStumble(nsGeoPosition* position);
class StumblerInfo final : public nsICellInfoListCallback,
public nsIWifiScanResultsReady
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICELLINFOLISTCALLBACK
NS_DECL_NSIWIFISCANRESULTSREADY
explicit StumblerInfo(nsGeoPosition* position)
: mPosition(position), mCellInfoResponsesExpected(0), mCellInfoResponsesReceived(0), mIsWifiInfoResponseReceived(0)
{}
void SetWifiInfoResponseReceived();
void SetCellInfoResponsesExpected(uint8_t count);
private:
~StumblerInfo() {}
void DumpStumblerInfo();
nsresult LocationInfoToString(nsACString& aLocDesc);
void CellNetworkInfoToString(nsACString& aCellDesc);
nsTArray<RefPtr<nsICellInfo>> mCellInfo;
nsCString mWifiDesc;
RefPtr<nsGeoPosition> mPosition;
int mCellInfoResponsesExpected;
int mCellInfoResponsesReceived;
bool mIsWifiInfoResponseReceived;
};
#endif // mozilla_system_mozstumbler_h__

View file

@ -0,0 +1,13 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "StumblerLogging.h"
mozilla::LogModule* GetLog()
{
static mozilla::LazyLogModule log("mozstumbler");
return log;
}

View file

@ -0,0 +1,18 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef STUMBLERLOGGING_H
#define STUMBLERLOGGING_H
#include "mozilla/Logging.h"
mozilla::LogModule* GetLog();
#define STUMBLER_DBG(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, ("STUMBLER - %s: " arg, __func__, ##__VA_ARGS__))
#define STUMBLER_LOG(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Info, ("STUMBLER - %s: " arg, __func__, ##__VA_ARGS__))
#define STUMBLER_ERR(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Error, ("STUMBLER -%s: " arg, __func__, ##__VA_ARGS__))
#endif

View file

@ -0,0 +1,151 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "UploadStumbleRunnable.h"
#include "StumblerLogging.h"
#include "mozilla/dom/Event.h"
#include "nsIInputStream.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURLFormatter.h"
#include "nsIXMLHttpRequest.h"
#include "nsNetUtil.h"
#include "nsVariant.h"
UploadStumbleRunnable::UploadStumbleRunnable(nsIInputStream* aUploadData)
: mUploadInputStream(aUploadData)
{
}
NS_IMETHODIMP
UploadStumbleRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = Upload();
if (NS_FAILED(rv)) {
WriteStumbleOnThread::UploadEnded(false);
}
return NS_OK;
}
nsresult
UploadStumbleRunnable::Upload()
{
nsresult rv;
RefPtr<nsVariant> variant = new nsVariant();
rv = variant->SetAsISupports(mUploadInputStream);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXMLHttpRequest> xhr = do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptSecurityManager> secman =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> systemPrincipal;
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURLFormatter> formatter =
do_CreateInstance("@mozilla.org/toolkit/URLFormatterService;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsString url;
rv = formatter->FormatURLPref(NS_LITERAL_STRING("geo.stumbler.url"), url);
NS_ENSURE_SUCCESS(rv, rv);
rv = xhr->Open(NS_LITERAL_CSTRING("POST"), NS_ConvertUTF16toUTF8(url), false, EmptyString(), EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
xhr->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), NS_LITERAL_CSTRING("gzip"));
xhr->SetMozBackgroundRequest(true);
// 60s timeout
xhr->SetTimeout(60 * 1000);
nsCOMPtr<EventTarget> target(do_QueryInterface(xhr));
RefPtr<nsIDOMEventListener> listener = new UploadEventListener(xhr);
const char* const sEventStrings[] = {
// nsIXMLHttpRequestEventTarget event types
"abort",
"error",
"load",
"timeout"
};
for (uint32_t index = 0; index < MOZ_ARRAY_LENGTH(sEventStrings); index++) {
nsAutoString eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
rv = target->AddEventListener(eventType, listener, false);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = xhr->Send(variant);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMPL_ISUPPORTS(UploadEventListener, nsIDOMEventListener)
UploadEventListener::UploadEventListener(nsIXMLHttpRequest* aXHR)
: mXHR(aXHR)
{
}
NS_IMETHODIMP
UploadEventListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsString type;
if (NS_FAILED(aEvent->GetType(type))) {
STUMBLER_ERR("Failed to get event type");
WriteStumbleOnThread::UploadEnded(false);
return NS_ERROR_FAILURE;
}
if (type.EqualsLiteral("load")) {
STUMBLER_DBG("Got load Event\n");
} else if (type.EqualsLiteral("error") && mXHR) {
STUMBLER_ERR("Upload Error");
} else {
STUMBLER_DBG("Receive %s Event", NS_ConvertUTF16toUTF8(type).get());
}
uint32_t statusCode = 0;
bool doDelete = false;
if (!mXHR) {
return NS_OK;
}
nsresult rv = mXHR->GetStatus(&statusCode);
if (NS_SUCCEEDED(rv)) {
STUMBLER_DBG("statuscode %d \n", statusCode);
}
if (200 == statusCode || 400 == statusCode) {
doDelete = true;
}
WriteStumbleOnThread::UploadEnded(doDelete);
nsCOMPtr<EventTarget> target(do_QueryInterface(mXHR));
const char* const sEventStrings[] = {
// nsIXMLHttpRequestEventTarget event types
"abort",
"error",
"load",
"timeout"
};
for (uint32_t index = 0; index < MOZ_ARRAY_LENGTH(sEventStrings); index++) {
nsAutoString eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
rv = target->RemoveEventListener(eventType, this, false);
}
mXHR = nullptr;
return NS_OK;
}

View file

@ -0,0 +1,46 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef UPLOADSTUMBLERUNNABLE_H
#define UPLOADSTUMBLERUNNABLE_H
#include "nsIDOMEventListener.h"
class nsIXMLHttpRequest;
class nsIInputStream;
/*
This runnable is managed by WriteStumbleOnThread only, see that class
for how this is scheduled.
*/
class UploadStumbleRunnable final : public Runnable
{
public:
explicit UploadStumbleRunnable(nsIInputStream* aUploadInputStream);
NS_IMETHOD Run() override;
private:
virtual ~UploadStumbleRunnable() {}
nsCOMPtr<nsIInputStream> mUploadInputStream;
nsresult Upload();
};
class UploadEventListener : public nsIDOMEventListener
{
public:
UploadEventListener(nsIXMLHttpRequest* aXHR);
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
protected:
virtual ~UploadEventListener() {}
nsCOMPtr<nsIXMLHttpRequest> mXHR;
};
#endif

View file

@ -0,0 +1,321 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "WriteStumbleOnThread.h"
#include "StumblerLogging.h"
#include "UploadStumbleRunnable.h"
#include "nsDumpUtils.h"
#include "nsGZFileWriter.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsPrintfCString.h"
#define MAXFILESIZE_KB (15 * 1024)
#define ONEDAY_IN_MSEC (24 * 60 * 60 * 1000)
#define MAX_UPLOAD_ATTEMPTS 20
mozilla::Atomic<bool> WriteStumbleOnThread::sIsFileWaitingForUpload(false);
mozilla::Atomic<bool> WriteStumbleOnThread::sIsAlreadyRunning(false);
WriteStumbleOnThread::UploadFreqGuard WriteStumbleOnThread::sUploadFreqGuard = {0};
#define FILENAME_INPROGRESS NS_LITERAL_CSTRING("stumbles.json.gz")
#define FILENAME_COMPLETED NS_LITERAL_CSTRING("stumbles.done.json.gz")
#define OUTPUT_DIR NS_LITERAL_CSTRING("mozstumbler")
class DeleteRunnable : public Runnable
{
public:
DeleteRunnable() {}
NS_IMETHOD
Run() override
{
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED,
getter_AddRefs(tmpFile),
OUTPUT_DIR,
nsDumpUtils::CREATE);
if (NS_SUCCEEDED(rv)) {
tmpFile->Remove(true);
}
// critically, this sets this flag to false so writing can happen again
WriteStumbleOnThread::sIsAlreadyRunning = false;
WriteStumbleOnThread::sIsFileWaitingForUpload = false;
return NS_OK;
}
private:
~DeleteRunnable() {}
};
bool
WriteStumbleOnThread::IsFileWaitingForUpload()
{
return sIsFileWaitingForUpload;
}
void
WriteStumbleOnThread::UploadEnded(bool deleteUploadFile)
{
if (!deleteUploadFile) {
sIsAlreadyRunning = false;
return;
}
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
nsCOMPtr<nsIRunnable> event = new DeleteRunnable();
target->Dispatch(event, NS_DISPATCH_NORMAL);
}
void
WriteStumbleOnThread::WriteJSON(Partition aPart)
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv;
rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Open a file for stumble failed");
return;
}
RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter(nsGZFileWriter::Append);
rv = gzWriter->Init(tmpFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("gzWriter init failed");
return;
}
/*
The json format is like below.
{items:[
{item},
{item},
{item}
]}
*/
// Need to add "]}" after the last item
if (aPart == Partition::End) {
rv = gzWriter->Write("]}");
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("gzWriter Write failed");
}
rv = gzWriter->Finish();
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream finish failed");
}
nsCOMPtr<nsIFile> targetFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(targetFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
nsAutoString targetFilename;
rv = targetFile->GetLeafName(targetFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Get Filename failed");
return;
}
rv = targetFile->Remove(true);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Remove File failed");
return;
}
// Rename tmpfile
rv = tmpFile->MoveTo(/* directory */ nullptr, targetFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Rename File failed");
return;
}
return;
}
// Need to add "{items:[" before the first item
if (aPart == Partition::Begining) {
rv = gzWriter->Write("{\"items\":[{");
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream write begining failed");
}
} else if (aPart == Partition::Middle) {
rv = gzWriter->Write(",{");
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream write middle failed");
}
}
rv = gzWriter->Write(mDesc.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream write mDesc failed");
}
// one item is ended with '}' (e.g. {item})
rv = gzWriter->Write("}");
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream write end failed");
}
rv = gzWriter->Finish();
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream finish failed");
}
// check if it is the end of this file
int64_t fileSize = 0;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return;
}
if (fileSize >= MAXFILESIZE_KB) {
WriteJSON(Partition::End);
return;
}
}
WriteStumbleOnThread::Partition
WriteStumbleOnThread::GetWritePosition()
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Open a file for stumble failed");
return Partition::Unknown;
}
int64_t fileSize = 0;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return Partition::Unknown;
}
if (fileSize == 0) {
return Partition::Begining;
} else if (fileSize >= MAXFILESIZE_KB) {
return Partition::End;
} else {
return Partition::Middle;
}
}
NS_IMETHODIMP
WriteStumbleOnThread::Run()
{
MOZ_ASSERT(!NS_IsMainThread());
bool b = sIsAlreadyRunning.exchange(true);
if (b) {
return NS_OK;
}
UploadFileStatus status = GetUploadFileStatus();
if (UploadFileStatus::NoFile != status) {
if (UploadFileStatus::ExistsAndReadyToUpload == status) {
sIsFileWaitingForUpload = true;
Upload();
return NS_OK;
}
} else {
Partition partition = GetWritePosition();
if (partition == Partition::Unknown) {
STUMBLER_ERR("GetWritePosition failed, skip once");
} else {
WriteJSON(partition);
}
}
sIsFileWaitingForUpload = false;
sIsAlreadyRunning = false;
return NS_OK;
}
/*
If the upload file exists, then check if it is one day old.
• if it is a day old -> ExistsAndReadyToUpload
• if it is less than the current day old -> Exists
• otherwise -> NoFile
The Exists case means that the upload and the stumbling is rate limited
per-day to the size of the one file.
*/
WriteStumbleOnThread::UploadFileStatus
WriteStumbleOnThread::GetUploadFileStatus()
{
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
int64_t fileSize;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return UploadFileStatus::NoFile;
}
if (fileSize <= 0) {
tmpFile->Remove(true);
return UploadFileStatus::NoFile;
}
PRTime lastModifiedTime;
tmpFile->GetLastModifiedTime(&lastModifiedTime);
if ((PR_Now() / PR_USEC_PER_MSEC) - lastModifiedTime >= ONEDAY_IN_MSEC) {
return UploadFileStatus::ExistsAndReadyToUpload;
}
return UploadFileStatus::Exists;
}
void
WriteStumbleOnThread::Upload()
{
MOZ_ASSERT(!NS_IsMainThread());
time_t seconds = time(0);
int day = seconds / (60 * 60 * 24);
if (sUploadFreqGuard.daySinceEpoch < day) {
sUploadFreqGuard.daySinceEpoch = day;
sUploadFreqGuard.attempts = 0;
}
sUploadFreqGuard.attempts++;
if (sUploadFreqGuard.attempts > MAX_UPLOAD_ATTEMPTS) {
STUMBLER_ERR("Too many upload attempts today");
sIsAlreadyRunning = false;
return;
}
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
int64_t fileSize;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
sIsAlreadyRunning = false;
return;
}
if (fileSize <= 0) {
sIsAlreadyRunning = false;
return;
}
// prepare json into nsIInputStream
nsCOMPtr<nsIInputStream> inStream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile);
if (NS_FAILED(rv)) {
sIsAlreadyRunning = false;
return;
}
RefPtr<nsIRunnable> uploader = new UploadStumbleRunnable(inStream);
NS_DispatchToMainThread(uploader);
}

View file

@ -0,0 +1,91 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef WriteStumbleOnThread_H
#define WriteStumbleOnThread_H
#include "mozilla/Atomics.h"
class DeleteRunnable;
/*
This class is the entry point to stumbling, in that it
receives the location+cell+wifi string and writes it
to disk, or instead, it calls UploadStumbleRunnable
to upload the data.
Writes will happen until the file is a max size, then stop.
Uploads will happen only when the file is one day old.
The purpose of these decisions is to have very simple rate-limiting
on the writes, as well as the uploads.
There is only one file active; it is either being used for writing,
or for uploading. If the file is ready for uploading, no further
writes will take place until this file has been uploaded.
This can mean writing might not take place for days until the uploaded
file is processed. This is correct by-design.
A notable limitation is that the upload is triggered by a location event,
this is used as an arbitrary and simple trigger. In future, there are
better events that can be used, such as detecting network activity.
This thread is guarded so that only one instance is active (see the
mozilla::Atomics used for this).
*/
class WriteStumbleOnThread : public mozilla::Runnable
{
public:
explicit WriteStumbleOnThread(const nsCString& aDesc)
: mDesc(aDesc)
{}
NS_IMETHOD Run() override;
static void UploadEnded(bool deleteUploadFile);
// Used externally to determine if cell+wifi scans should happen
// (returns false for that case).
static bool IsFileWaitingForUpload();
private:
friend class DeleteRunnable;
enum class Partition {
Begining,
Middle,
End,
Unknown
};
enum class UploadFileStatus {
NoFile, Exists, ExistsAndReadyToUpload
};
~WriteStumbleOnThread() {}
Partition GetWritePosition();
UploadFileStatus GetUploadFileStatus();
void WriteJSON(Partition aPart);
void Upload();
nsCString mDesc;
// Only run one instance of this
static mozilla::Atomic<bool> sIsAlreadyRunning;
static mozilla::Atomic<bool> sIsFileWaitingForUpload;
// Limit the upload attempts per day. If the device is rebooted
// this resets the allowed attempts, which is acceptable.
struct UploadFreqGuard {
int attempts;
int daySinceEpoch;
};
static UploadFreqGuard sUploadFreqGuard;
};
#endif

View file

@ -0,0 +1,58 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, builtinclass, uuid(df31c280-1ef1-11e5-867f-0800200c9a66)]
interface nsIAudioManager : nsISupports
{
/**
* Microphone muted?
*/
attribute boolean microphoneMuted;
/**
* Set the phone's audio mode.
*/
const long PHONE_STATE_INVALID = -2;
const long PHONE_STATE_CURRENT = -1;
const long PHONE_STATE_NORMAL = 0;
const long PHONE_STATE_RINGTONE = 1;
const long PHONE_STATE_IN_CALL = 2;
const long PHONE_STATE_IN_COMMUNICATION = 3;
attribute long phoneState;
/**
* Configure a particular device ("force") to be used for one of the uses
* (communication, media playback, etc.)
*/
const long FORCE_NONE = 0; // the default
const long FORCE_SPEAKER = 1;
const long FORCE_HEADPHONES = 2;
const long FORCE_BT_SCO = 3;
const long FORCE_BT_A2DP = 4;
const long FORCE_WIRED_ACCESSORY = 5;
const long FORCE_BT_CAR_DOCK = 6;
const long FORCE_BT_DESK_DOCK = 7;
const long FORCE_ANALOG_DOCK = 8;
const long FORCE_DIGITAL_DOCK = 9;
const long FORCE_NO_BT_A2DP = 10;
const long USE_COMMUNICATION = 0;
const long USE_MEDIA = 1;
const long USE_RECORD = 2;
const long USE_DOCK = 3;
void setForceForUse(in long usage, in long force);
long getForceForUse(in long usage);
/**
* These functions would be used when we enable the new volume control API
* (mozAudioChannelManager). The range of volume index is from 0 to N.
* More details on : https://gist.github.com/evanxd/41d8e2d91c5201a42bfa
*/
void setAudioChannelVolume(in unsigned long channel, in unsigned long index);
unsigned long getAudioChannelVolume(in unsigned long channel);
unsigned long getMaxAudioChannelVolume(in unsigned long channel);
};

View file

@ -0,0 +1,268 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, uuid(6b66446a-7000-438f-8e1b-b56b4cbf4fa9)]
interface nsIDataCall : nsISupports
{
/**
* Data call fail cause. One of the nsIDataCallInterface.DATACALL_FAIL_*
* values.
*/
readonly attribute long failCause;
/**
* If failCause != nsIDataCallInterface.DATACALL_FAIL_NONE, this field
* indicates the suggested retry back-off timer. The unit is milliseconds.
*/
readonly attribute long suggestedRetryTime;
/**
* Context ID, uniquely identifies this call.
*/
readonly attribute long cid;
/**
* Data call network state. One of the nsIDataCallInterface.DATACALL_STATE_*
* values.
*/
readonly attribute long active;
/**
* Data call connection type. One of the
* nsIDataCallInterface.DATACALL_PDP_TYPE_* values.
*/
readonly attribute long pdpType;
/**
* The network interface name.
*/
readonly attribute DOMString ifname;
/**
* A space-delimited list of addresses with optional "/" prefix length.
*/
readonly attribute DOMString addresses;
/**
* A space-delimited list of DNS server addresses.
*/
readonly attribute DOMString dnses;
/**
* A space-delimited list of default gateway addresses.
*/
readonly attribute DOMString gateways;
/**
* A space-delimited list of Proxy Call State Control Function addresses for
* IMS client.
*/
readonly attribute DOMString pcscf;
/**
* MTU received from network, -1 if not set or invalid.
*/
readonly attribute long mtu;
};
[scriptable, uuid(e119c54b-9354-4ad6-a1ee-18608bde9320)]
interface nsIDataCallInterfaceListener : nsISupports
{
/**
* Notify data call interface listeners about unsolicited data call state
* changes.
*/
void notifyDataCallListChanged(in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
};
[scriptable, uuid(db0b640a-3b3a-4f48-84dc-256e176876c2)]
interface nsIDataCallCallback : nsISupports
{
/**
* Called when setupDataCall() returns succesfully.
*/
void notifySetupDataCallSuccess(in nsIDataCall dataCall);
/**
* Called when getDataCallList() returns succesfully.
*/
void notifyGetDataCallListSuccess(in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
/**
* Called when request returns succesfully.
*/
void notifySuccess();
/**
* Called when request returns error.
*/
void notifyError(in AString errorMsg);
};
[scriptable, uuid(ec219021-8623-4b9f-aba5-4db58c60684f)]
interface nsIDataCallInterface : nsISupports
{
/**
* Data fail causes, defined in TS 24.008.
*/
const long DATACALL_FAIL_NONE = 0;
const long DATACALL_FAIL_OPERATOR_BARRED = 0x08;
const long DATACALL_FAIL_INSUFFICIENT_RESOURCES = 0x1A;
const long DATACALL_FAIL_MISSING_UKNOWN_APN = 0x1B;
const long DATACALL_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C;
const long DATACALL_FAIL_USER_AUTHENTICATION = 0x1D;
const long DATACALL_FAIL_ACTIVATION_REJECT_GGSN = 0x1E;
const long DATACALL_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
const long DATACALL_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20;
const long DATACALL_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21;
const long DATACALL_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22;
const long DATACALL_FAIL_NSAPI_IN_USE = 0x23;
const long DATACALL_FAIL_ONLY_IPV4_ALLOWED = 0x32;
const long DATACALL_FAIL_ONLY_IPV6_ALLOWED = 0x33;
const long DATACALL_FAIL_ONLY_SINGLE_BEARER_ALLOWED = 0x34;
const long DATACALL_FAIL_PROTOCOL_ERRORS = 0x6F;
/* Not mentioned in the specification */
const long DATACALL_FAIL_VOICE_REGISTRATION_FAIL = -1;
const long DATACALL_FAIL_DATA_REGISTRATION_FAIL = -2;
const long DATACALL_FAIL_SIGNAL_LOST = -3;
const long DATACALL_FAIL_PREF_RADIO_TECH_CHANGED = -4;
const long DATACALL_FAIL_RADIO_POWER_OFF = -5;
const long DATACALL_FAIL_TETHERED_CALL_ACTIVE = -6;
const long DATACALL_FAIL_ERROR_UNSPECIFIED = 0xFFFF;
/**
* Data call network state.
*/
const long DATACALL_STATE_INACTIVE = 0;
const long DATACALL_STATE_ACTIVE_DOWN = 1;
const long DATACALL_STATE_ACTIVE_UP = 2;
/**
* Data call authentication type. Must match the values in ril_consts
* RIL_DATACALL_AUTH_TO_GECKO array.
*/
const long DATACALL_AUTH_NONE = 0;
const long DATACALL_AUTH_PAP = 1;
const long DATACALL_AUTH_CHAP = 2;
const long DATACALL_AUTH_PAP_OR_CHAP = 3;
/**
* Data call protocol type. Must match the values in ril_consts
* RIL_DATACALL_PDP_TYPES array.
*/
const long DATACALL_PDP_TYPE_IPV4 = 0;
const long DATACALL_PDP_TYPE_IPV4V6 = 1;
const long DATACALL_PDP_TYPE_IPV6 = 2;
/**
* Reason for deactivating data call.
*/
const long DATACALL_DEACTIVATE_NO_REASON = 0;
const long DATACALL_DEACTIVATE_RADIO_SHUTDOWN = 1;
/**
* Setup data call.
*
* @param apn
* Apn to connect to.
* @param username
* Username for apn.
* @param password
* Password for apn.
* @param authType
* Authentication type. One of the DATACALL_AUTH_* values.
* @param pdpType
* Connection type. One of the DATACALL_PDP_TYPE_* values.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySetupDataCallSuccess() will be called with the
* new nsIDataCall.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable', 'OpNotAllowedBeforeRegToNw',
* 'OpNotAllowedDuringVoiceCall', 'RequestNotSupported' or 'GenericFailure'.
*/
void setupDataCall(in AString apn, in AString username,
in AString password, in long authType,
in long pdpType,
in nsIDataCallCallback callback);
/**
* Deactivate data call.
*
* @param cid
* Context id.
* @param reason
* Disconnect Reason. One of the DATACALL_DEACTIVATE_* values.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySuccess() will be called.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable' or 'GenericFailure'.
*/
void deactivateDataCall(in long cid,
in long reason,
in nsIDataCallCallback callback);
/**
* Get current data call list.
*
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifyGetDataCallListSuccess() will be called with the
* list of nsIDataCall(s).
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable' or 'GenericFailure'.
*/
void getDataCallList(in nsIDataCallCallback callback);
/**
* Set data registration state.
*
* @param attach
* whether to attach data registration or not.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySuccess() will be called.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable', 'SubscriptionNotAvailable' or 'GenericFailure'.
*/
void setDataRegistration(in boolean attach,
in nsIDataCallCallback callback);
/**
* Register to receive unsolicited events from this nsIDataCallInterface.
*/
void registerListener(in nsIDataCallInterfaceListener listener);
/**
* Unregister to stop receiving unsolicited events from this
* nsIDataCallInterface.
*/
void unregisterListener(in nsIDataCallInterfaceListener listener);
};
[scriptable, uuid(64700406-7429-4743-a6ae-f82e9877fd0d)]
interface nsIDataCallInterfaceService : nsISupports
{
/**
* Get the corresponding data call interface.
*
* @param clientId
* clientId of the data call interface to get.
*/
nsIDataCallInterface getDataCallInterface(in long clientId);
};

View file

@ -0,0 +1,81 @@
/* 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/. */
#include "nsISupports.idl"
#include "nsINetworkInterface.idl"
[scriptable, uuid(b8bcd6aa-5b06-4362-a68c-317878429e51)]
interface nsIRilNetworkInfo : nsINetworkInfo
{
readonly attribute unsigned long serviceId;
readonly attribute DOMString iccId;
/* The following attributes are for MMS proxy settings. */
readonly attribute DOMString mmsc; // Empty string if not set.
readonly attribute DOMString mmsProxy; // Empty string if not set.
readonly attribute long mmsPort; // -1 if not set.
/**
* Get the list of pcscf addresses, could be IPv4 or IPv6.
*
* @param count
* The length of the list of pcscf addresses.
*
* @returns the list of pcscf addresses.
*/
void getPcscf([optional] out unsigned long count,
[array, size_is(count), retval] out wstring pcscf);
};
[scriptable, function, uuid(cb2f0f5b-67f4-4c14-93e8-01e66b630464)]
interface nsIDeactivateDataCallsCallback : nsISupports
{
/**
* Callback function used to notify when all data calls are disconnected.
*/
void notifyDataCallsDisconnected();
};
[scriptable, uuid(e3feec20-36b4-47de-a7a5-e32a65f20186)]
interface nsIDataCallHandler : nsISupports
{
/**
* PDP APIs
*
* @param networkType
* Mobile network type, that is,
* nsINetworkInterface.NETWORK_TYPE_MOBILE or one of the
* nsINetworkInterface.NETWORK_TYPE_MOBILE_* values.
*/
void setupDataCallByType(in long networkType);
void deactivateDataCallByType(in long networkType);
long getDataCallStateByType(in long networkType);
/**
* Deactivate all data calls.
*
* @param callback
* Callback to notify when all data calls are disconnected.
*/
void deactivateDataCalls(in nsIDeactivateDataCallsCallback callback);
/**
* Called to reconsider data call state.
*/
void updateRILNetworkInterface();
};
[scriptable, uuid(2c46e37d-88dc-4d25-bb37-e1c0d3e9cb5f)]
interface nsIDataCallManager : nsISupports
{
readonly attribute long dataDefaultServiceId;
/**
* Get the corresponding data call handler.
*
* @param clientId
* clientId of the data call handler to get.
*/
nsIDataCallHandler getDataCallHandler(in unsigned long clientId);
};

View file

@ -0,0 +1,18 @@
/* 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/. */
#include "nsIDataCallInterfaceService.idl"
[scriptable, uuid(f008d00c-e2b8-49b2-8f88-19111577938e)]
interface nsIGonkDataCallInterfaceService : nsIDataCallInterfaceService
{
/**
* Called by RadioInterface or lower layer to notify about data call list
* changes.
*/
void notifyDataCallListChanged(in unsigned long clientId,
in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
};

View file

@ -0,0 +1,108 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, uuid(4816a559-5620-4cb5-8433-ff0b25e6622f)]
interface nsINetworkInfo : nsISupports
{
const long NETWORK_STATE_UNKNOWN = -1;
const long NETWORK_STATE_CONNECTING = 0;
const long NETWORK_STATE_CONNECTED = 1;
const long NETWORK_STATE_DISCONNECTING = 2;
const long NETWORK_STATE_DISCONNECTED = 3;
const long NETWORK_STATE_ENABLED = 4;
const long NETWORK_STATE_DISABLED = 5;
/**
* Current network state, one of the NETWORK_STATE_* constants.
*
* When this changes, network interface implementations notify with
* updateNetworkInterface() API.
*/
readonly attribute long state;
const long NETWORK_TYPE_UNKNOWN = -1;
const long NETWORK_TYPE_WIFI = 0;
const long NETWORK_TYPE_MOBILE = 1;
const long NETWORK_TYPE_MOBILE_MMS = 2;
const long NETWORK_TYPE_MOBILE_SUPL = 3;
const long NETWORK_TYPE_WIFI_P2P = 4;
const long NETWORK_TYPE_MOBILE_IMS = 5;
const long NETWORK_TYPE_MOBILE_DUN = 6;
const long NETWORK_TYPE_MOBILE_FOTA = 7;
const long NETWORK_TYPE_ETHERNET = 8;
/**
* Network type. One of the NETWORK_TYPE_* constants.
*/
readonly attribute long type;
/**
* Interface name of the network interface this network info belongs to.
*/
readonly attribute DOMString name;
/**
* Get the list of ip addresses and prefix lengths, ip address could be IPv4
* or IPv6, typically 1 IPv4 or 1 IPv6 or one of each.
*
* @param ips
* The list of ip addresses retrieved.
* @param prefixLengths
* The list of prefix lengths retrieved.
*
* @returns the length of the lists.
*/
void getAddresses([array, size_is(count)] out wstring ips,
[array, size_is(count)] out unsigned long prefixLengths,
[retval] out unsigned long count);
/**
* Get the list of gateways, could be IPv4 or IPv6, typically 1 IPv4 or 1
* IPv6 or one of each.
*
* @param count
* The length of the list of gateways
*
* @returns the list of gateways.
*/
void getGateways([optional] out unsigned long count,
[array, size_is(count), retval] out wstring gateways);
/**
* Get the list of dnses, could be IPv4 or IPv6.
*
* @param count
* The length of the list of dnses.
*
* @returns the list of dnses.
*/
void getDnses([optional] out unsigned long count,
[array, size_is(count), retval] out wstring dnses);
};
[scriptable, uuid(8b1345fa-b34c-41b3-8d21-09f961bf8887)]
interface nsINetworkInterface : nsISupports
{
/**
* The network information about this network interface.
*/
readonly attribute nsINetworkInfo info;
/**
* The host name of the http proxy server.
*/
readonly attribute DOMString httpProxyHost;
/*
* The port number of the http proxy server.
*/
readonly attribute long httpProxyPort;
/*
* The Maximun Transmit Unit for this network interface, -1 if not set.
*/
readonly attribute long mtu;
};

View file

@ -0,0 +1,40 @@
/* 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/. */
#include "nsISupports.idl"
interface nsINetworkInfo;
[scriptable, uuid(55779d32-1e28-4f43-af87-09d04bc3cce9)]
interface nsINetworkInterfaceList : nsISupports
{
/**
* Number of the network interfaces that is available.
*/
long getNumberOfInterface();
/**
* Get the i-th interface info info from the list.
* @param interfaceIndex index of interface, from 0 to number of interface - 1.
*/
nsINetworkInfo getInterfaceInfo(in long interfaceIndex);
};
[scriptable, uuid(21d7fc8b-28c4-4a4f-a15e-1f9defbc2cec)]
interface nsINetworkInterfaceListService : nsISupports
{
const long LIST_NOT_INCLUDE_MMS_INTERFACES = (1 << 0);
const long LIST_NOT_INCLUDE_SUPL_INTERFACES = (1 << 1);
const long LIST_NOT_INCLUDE_IMS_INTERFACES = (1 << 2);
const long LIST_NOT_INCLUDE_DUN_INTERFACES = (1 << 3);
const long LIST_NOT_INCLUDE_FOTA_INTERFACES = (1 << 4);
/**
* Obtain a list of network interfaces that satisfy the specified condition.
* @param condition flags that specify the interfaces to be returned. This
* can be OR combination of LIST_* flags, or zero to make all available
* interfaces returned.
*/
nsINetworkInterfaceList getDataInterfaceList(in long condition);
};

View file

@ -0,0 +1,135 @@
/* 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/. */
#include "nsISupports.idl"
interface nsINetworkInfo;
interface nsINetworkInterface;
/**
* Manage network interfaces.
*/
[scriptable, uuid(1ba9346b-53b5-4660-9dc6-58f0b258d0a6)]
interface nsINetworkManager : nsISupports
{
/**
* Register the given network interface with the network manager.
*
* Consumers will be notified with the 'network-interface-registered'
* observer notification.
*
* Throws if there's already an interface registered with the same network id.
*
* @param network
* Network interface to register.
*/
void registerNetworkInterface(in nsINetworkInterface network);
/**
* Update the routes and DNSes according the state of the given network.
*
* Consumers will be notified with the 'network-connection-state-changed'
* observer notification.
*
* Throws an exception if the specified network interface object isn't
* registered.
*
* @param network
* Network interface to update.
*/
void updateNetworkInterface(in nsINetworkInterface network);
/**
* Unregister the given network interface from the network manager.
*
* Consumers will be notified with the 'network-interface-unregistered'
* observer notification.
*
* Throws an exception if the specified network interface object isn't
* registered.
*
* @param network
* Network interface to unregister.
*/
void unregisterNetworkInterface(in nsINetworkInterface network);
/**
* Object containing all known network information, keyed by their
* network id. Network id is composed of a sub-id + '-' + network
* type. For mobile network types, sub-id is 'ril' + service id; for
* non-mobile network types, sub-id is always 'device'.
*/
readonly attribute jsval allNetworkInfo;
/**
* Priority list of network types. An array of
* nsINetworkInterface::NETWORK_TYPE_* constants.
*
* The piror position of the type indicates the higher priority. The priority
* is used to determine route when there are multiple connected networks.
*/
attribute jsval networkTypePriorityList;
/**
* The preferred network type. One of the
* nsINetworkInterface::NETWORK_TYPE_* constants.
*
* This attribute is used for setting default route to favor
* interfaces with given type. This can be overriden by calling
* overrideDefaultRoute().
*/
attribute long preferredNetworkType;
/**
* The network information of the network interface handling all network
* traffic.
*
* When this changes, the 'network-active-changed' observer
* notification is dispatched.
*/
readonly attribute nsINetworkInfo activeNetworkInfo;
/**
* Override the default behaviour for preferredNetworkType and route
* all network traffic through the the specified interface.
*
* Consumers can observe changes to the active network by subscribing to
* the 'network-active-changed' observer notification.
*
* @param network
* Network to route all network traffic to. If this is null,
* a previous override is canceled.
*/
long overrideActive(in nsINetworkInterface network);
/**
* Add host route to the specified network into routing table.
*
* @param network
* The network information for the host to be routed to.
* @param host
* The host to be added.
* The host will be resolved in advance if it's not an ip-address.
*
* @return a Promise
* resolved if added; rejected, otherwise.
*/
jsval addHostRoute(in nsINetworkInfo network,
in DOMString host);
/**
* Remove host route to the specified network from routing table.
*
* @param network
* The network information for the routing to be removed from.
* @param host
* The host routed to the network.
* The host will be resolved in advance if it's not an ip-address.
*
* @return a Promise
* resolved if removed; rejected, otherwise.
*/
jsval removeHostRoute(in nsINetworkInfo network,
in DOMString host);
};

View file

@ -0,0 +1,619 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, function, uuid(91824160-fb25-11e1-a21f-0800200c9a66)]
interface nsIWifiTetheringCallback : nsISupports
{
/**
* Callback function used to report status to WifiManager.
*
* @param error
* An error message if the operation wasn't successful,
* or `null` if it was.
*/
void wifiTetheringEnabledChange(in jsval error);
};
[scriptable, function, uuid(9c128e68-5e4b-4626-bb88-84ec54cce5d8)]
interface nsINetworkStatsCallback : nsISupports
{
void networkStatsAvailable(in boolean success,
in unsigned long rxBytes,
in unsigned long txBytes,
in unsigned long long timestamp);
};
[scriptable, function, uuid(0706bfa2-ac2d-11e2-9a8d-7b6d988d4767)]
interface nsINetworkUsageAlarmCallback : nsISupports
{
void networkUsageAlarmResult(in jsval error);
};
[scriptable, function, uuid(9ede8720-f8bc-11e2-b778-0800200c9a66)]
interface nsIWifiOperationModeCallback : nsISupports
{
/**
* Callback function used to report result to WifiManager.
*
* @param error
* An error message if the operation wasn't successful,
* or `null` if it was.
*/
void wifiOperationModeResult(in jsval error);
};
[scriptable, function, uuid(097878b0-19fc-11e3-8ffd-0800200c9a66)]
interface nsISetDhcpServerCallback : nsISupports
{
/**
* Callback function used to report the DHCP server set result
*
* @param error
* An error message if the operation wasn't successful,
* or `null` if it was.
*/
void dhcpServerResult(in jsval error);
};
[scriptable, function, uuid(32407c50-46c7-11e3-8f96-0800200c9a66)]
interface nsIUsbTetheringCallback : nsISupports
{
/**
* Callback function used to report status of enabling usb tethering.
*
* @param error
* An error message if the operation wasn't successful,
* or `null` if it was.
*/
void usbTetheringEnabledChange(in jsval error);
};
[scriptable, function, uuid(055fd560-46ad-11e3-8f96-0800200c9a66)]
interface nsIEnableUsbRndisCallback : nsISupports
{
/**
* Callback function used to report the status of enabling/disabling usb rndis.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param enable
* Boolean to indicate if we are enabling or disabling usb rndis.
*/
void enableUsbRndisResult(in boolean success, in boolean enable);
};
[scriptable, function, uuid(4f08cc30-46ad-11e3-8f96-0800200c9a66)]
interface nsIUpdateUpStreamCallback : nsISupports
{
/**
* Callback function used to report the result of updating upstream.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param externalIfname
* The external interface name.
*/
void updateUpStreamResult(in boolean success, in DOMString externalIfname);
};
[scriptable, function, uuid(eedca6c0-1310-11e4-9191-0800200c9a66)]
interface nsISetDnsCallback : nsISupports
{
/**
* Callback function used to report the result of setting DNS server.
*
* @param error
* An error message if the operation wasn't successful,
* or `null` if it was.
*/
void setDnsResult(in jsval error);
};
[scriptable, function, uuid(5d0e1a60-1187-11e4-9191-0800200c9a66)]
interface nsINativeCommandCallback : nsISupports
{
/**
* Callback function used to report the result of a network native command.
*
* @param success
* Boolean to indicate the operation is successful or not.
*/
void nativeCommandResult(in boolean success);
};
[scriptable, function, uuid(694abb80-1187-11e4-9191-0800200c9a66)]
interface nsIDhcpRequestCallback : nsISupports
{
/**
* Callback function used to report the result of DHCP client request.
*
* @param success
* Boolean to indicate the operation is successful or not.
*
* @param dhcpInfo
* An object to represent the successful DHCP request:
*
* - gateway_str: string
* - dns1_str: string
* - dns2_str: string
* - mask_str: string
* - server_str: string
* - vendor_str: string
* - lease: long
* - mask: long
* - ipaddr: long
* - gateway: long
* - dns1: long
* - dns2: long
* - server: long
*/
void dhcpRequestResult(in boolean success, in jsval dhcpInfo);
};
[scriptable, function, uuid(88e3ee22-f1b3-4fa0-8a5d-793fb827c42c)]
interface nsIGetInterfacesCallback : nsISupports
{
/**
* Callback function used to return the list of existing network interfaces.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param interfaceList
* An array of interface name.
*/
void getInterfacesResult(in boolean success, in jsval interfaceList);
};
[scriptable, function, uuid(064e02a3-d2c0-42c5-a293-1efa84056100)]
interface nsIGetInterfaceConfigCallback : nsISupports
{
/**
* Callback function used to return the network config of a given interface.
*
* @param success
* Boolean to indicate the operation is successful or not.
* @param result
* .ip: Ip address.
* .prefix: mask length.
* .link: network link properties.
* .mac: mac address.
*/
void getInterfaceConfigResult(in boolean success, in jsval result);
};
[scriptable, function, uuid(b370f360-6ba8-4517-a4f9-31e8f004ee91)]
interface nsISetInterfaceConfigCallback : nsISupports
{
/**
* Callback function used to set network config for a specified interface.
*
* @param success
* Boolean to indicate the operation is successful or not.
*/
void setInterfaceConfigResult(in boolean success);
};
/**
* Provide network services.
*/
[scriptable, uuid(e16fe98f-9f63-48fe-82ba-8d1a1b7c6a57)]
interface nsINetworkService : nsISupports
{
const long MODIFY_ROUTE_ADD = 0;
const long MODIFY_ROUTE_REMOVE = 1;
/**
* Enable or disable Wifi Tethering
*
* @param enabled
* Boolean that indicates whether tethering should be enabled (true) or disabled (false).
* @param config
* The Wifi Tethering configuration from settings db.
* @param callback
* Callback function used to report status to WifiManager.
*/
void setWifiTethering(in boolean enabled,
in jsval config,
in nsIWifiTetheringCallback callback);
/**
* Enable or disable DHCP server
*
* @param enabled
* Boolean that indicates enabling or disabling DHCP server.
*
* @param config
* Config used to enable the DHCP server. It contains
* .startIp start of the ip lease range (string)
* .endIp end of the ip lease range (string)
* .serverIp ip of the DHCP server (string)
* .maskLength the length of the subnet mask
* .ifname the interface name
*
* As for disabling the DHCP server, put this value |null|.
*
* @param callback
* Callback function used to report status.
*/
void setDhcpServer(in boolean enabled,
in jsval config,
in nsISetDhcpServerCallback callback);
/**
* Retrieve network interface stats.
*
* @param networkName
* Select the Network interface to request estats.
*
* @param callback
* Callback to notify result and provide stats, connectionType
* and the date when stats are retrieved
*/
void getNetworkInterfaceStats(in DOMString networkName, in nsINetworkStatsCallback callback);
/**
* Set Alarm of usage per interface
*
* @param networkName
* Select the Network interface to set an alarm.
*
* @param threshold
* Amount of data that will trigger the alarm.
*
* @param callback
* Callback to notify the result.
*
* @return false if there is no interface registered for the networkType param.
*/
boolean setNetworkInterfaceAlarm(in DOMString networkName,
in long long threshold,
in nsINetworkUsageAlarmCallback callback);
/**
* Reload Wifi firmware to specific operation mode.
*
* @param interfaceName
* Wifi Network interface name.
*
* @param mode
* AP - Access pointer mode.
* P2P - Peer to peer connection mode.
* STA - Station mode.
*
* @param callback
* Callback to notify Wifi firmware reload result.
*/
void setWifiOperationMode(in DOMString interfaceName,
in DOMString mode,
in nsIWifiOperationModeCallback callback);
/**
* Set USB tethering.
*
* @param enabled
* Boolean to indicate we are going to enable or disable usb tethering.
* @param config
* The usb tethering configuration.
* @param callback
* Callback function used to report the result enabling/disabling usb tethering.
*/
void setUSBTethering(in boolean enabled,
in jsval config,
in nsIUsbTetheringCallback callback);
/**
* Reset routing table.
*
* @param interfaceName
* The name of the network interface we want to remove from the routing
* table.
*
* @param callback
* Callback to notify the result of resetting routing table.
*/
void resetRoutingTable(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Set DNS.
*
* @param interfaceName
* The network interface name of the DNS we want to set.
* @param dnsesCount
* Number of elements in dnses.
* @param dnses
* Dnses to set.
* @param gatewaysCount
* Number of elements in gateways.
* @param gateways
* Gateways for the dnses, the most suitable, usually the one with the
* same address family, will be selected for each dns.
*
* @param callback
* Callback to notify the result of setting DNS server.
*/
void setDNS(in DOMString interfaceName,
in unsigned long dnsesCount,
[array, size_is(dnsesCount)] in wstring dnses,
in unsigned long gatewaysCount,
[array, size_is(gatewaysCount)] in wstring gateways,
in nsISetDnsCallback callback);
/**
* Set default route.
*
* @param interfaceName
* The network interface name of the default route we want to set.
* @param count
* Number of elements in gateways.
* @param gateways
* Default gateways for setting default route.
*
* @param callback
* Callback to notify the result of setting default route.
*/
void setDefaultRoute(in DOMString interfaceName,
in unsigned long count,
[array, size_is(count)] in wstring gateways,
in nsINativeCommandCallback callback);
/**
* Remove default route.
*
* @param interfaceName
* The network interface name of the default route we want to remove.
* @param count
* Number of elements in gateways.
* @param gatways
* Default gateways for removing default route.
*
* @param callback
* Callback to notify the result of removing default route.
*/
void removeDefaultRoute(in DOMString interfaceName,
in unsigned long count,
[array, size_is(count)] in wstring gateways,
in nsINativeCommandCallback callback);
/**
* Modify route.
*
* @param action
* nsINetworkService.MODIFY_ROUTE_ADD to add route and
* nsINetworkService.MODIFY_ROUTE_REMOVE to remove.
* @param interfaceName
* Network interface name for the output of the host route.
* @param host
* Host ip we want to remove route for.
* @param prefixLength
* The prefix length of the route we'd like to modify.
* @param [optional] gateway
* Gateway ip for the output of the host route.
*
* @return A deferred promise that resolves on success or rejects with a
* specified reason otherwise.
*/
jsval modifyRoute(in long action,
in DOMString interfaceName,
in DOMString host,
in long prefixLength,
[optional] in DOMString gateway);
/**
* Add route to secondary routing table.
*
* @param interfaceName
* The network interface for this route.
* @param route
* The route info should have the following fields:
* .ip: destination ip address
* .prefix: destination prefix
* .gateway: gateway ip address
*/
void addSecondaryRoute(in DOMString interfaceName, in jsval route,
in nsINativeCommandCallback callback);
/**
* Remove route from secondary routing table.
*
* @param interfaceName
* The network interface for the route we want to remove.
* @param route
* The route info should have the following fields:
* .ip: destination ip address
* .prefix: destination prefix
* .gateway: gateway ip address
*/
void removeSecondaryRoute(in DOMString interfaceName, in jsval route,
in nsINativeCommandCallback callback);
/**
* Enable or disable usb rndis.
*
* @param enable
* Boolean to indicate we want enable or disable usb rndis.
* @param callback
* Callback function to report the result.
*/
void enableUsbRndis(in boolean enable,
in nsIEnableUsbRndisCallback callback);
/**
* Update upstream.
*
* @param previous
* The previous internal and external interface.
* @param current
* The current internal and external interface.
* @param callback
* Callback function to report the result.
*/
void updateUpStream(in jsval previous,
in jsval current,
in nsIUpdateUpStreamCallback callback);
/*
* Obtain interfaces list.
*
* @param callback
* Callback function to return the result.
*/
void getInterfaces(in nsIGetInterfacesCallback callback);
/**
* Get config of a network interface.
*
* @param ifname
* Target interface.
* @param callback
* Callback function to report the result.
*/
void getInterfaceConfig(in DOMString ifname, in nsIGetInterfaceConfigCallback callback);
/**
* Set config for a network interface.
*
* @param config
* .ifname: Target interface.
* .ip: Ip address.
* .prefix: mask length.
* .link: network link properties.
* @param callback
* Callback function to report the result.
*/
void setInterfaceConfig(in jsval config, in nsISetInterfaceConfigCallback callback);
/**
* Configure a network interface.
*
* @param config
* An object containing the detail that we want to configure the interface:
*
* - ifname: string
* - ipaddr: long
* - mask: long
* - gateway: long
* - dns1: long
* - dns2: long
*
* @param callback
* Callback to notify the result of configurating network interface.
*/
void configureInterface(in jsval config,
in nsINativeCommandCallback callback);
/**
* Issue a DHCP client request.
*
* @param networkInterface
* The network interface which we wnat to do the DHCP request on.
*
* @param callback
* Callback to notify the result of the DHCP request.
*/
void dhcpRequest(in DOMString interfaceName,
in nsIDhcpRequestCallback callback);
/**
* Stop Dhcp daemon.
*
* @param ifname
* Target interface.
*
* @param callback
* Callback to notify the result of stopping dhcp request.
*/
void stopDhcp(in DOMString ifname,
in nsINativeCommandCallback callback);
/**
* Enable a network interface.
*
* @param networkInterface
* The network interface name which we want to enable.
*
* @param callback
* Callback to notify the result of disabling network interface.
*/
void enableInterface(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Disable a network interface.
*
* @param networkInterface
* The network interface name which we want to disable.
*
* @param callback
* Callback to notify the result of disabling network interface.
*/
void disableInterface(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Reset all connections on a specified network interface.
*
* @param interfaceName
* The network interface name which we want to reset.
*
* @param callback
* Callback to notify the result of resetting connections.
*/
void resetConnections(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Create network (required to call prior to any networking operation).
*
* @param interfaceName
* The network interface name which we want to create network for.
*
* @param callback
* Callback to notify the result of creating network.
*/
void createNetwork(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Destroy network.
*
* @param interfaceName
* The network interface name of the network we want to destroy.
*
* @param callback
* Callback to notify the result of destroying network.
*/
void destroyNetwork(in DOMString interfaceName,
in nsINativeCommandCallback callback);
/**
* Query the netId associated with given network interface name.
*
* @param interfaceName
* The network interface name which we want to query.
*
* @return A deferred promise that resolves with a string to indicate.
* the queried netId on success and rejects if the interface name
* is invalid.
*
*/
jsval getNetId(in DOMString interfaceName);
/**
* Set maximum transmission unit on a network interface.
*
* @param interfaceName
* The name of the network interface that we want to set mtu.
* @param mtu
* Size of maximum tranmission unit.
*
* @param callback
* Callback to notify the result of setting mtu.
*/
void setMtu(in DOMString interfaceName, in long mtu,
in nsINativeCommandCallback callback);
};

View file

@ -0,0 +1,18 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, uuid(98e31d3b-6cad-4cab-b4b3-4afff566ea65)]
interface nsINetworkEventListener : nsISupports {
void onEvent(in jsval result);
};
[scriptable, uuid(f9d9c694-0aac-4f9a-98ac-7788f954239a)]
interface nsINetworkWorker : nsISupports {
void start(in nsINetworkEventListener listener);
void shutdown();
[implicit_jscontext]
void postMessage(in jsval options);
};

Some files were not shown because too many files have changed in this diff Show more