Merge mozilla-central and b2g-inbound

This commit is contained in:
Ed Morley 2013-09-13 15:14:11 +01:00
commit b1d4cc4939
30 changed files with 1049 additions and 179 deletions

View File

@ -249,13 +249,8 @@ ContentPermissionPrompt.prototype = {
return;
}
// When it's an app, get the manifest to add the l10n application name.
let app = DOMApplicationRegistry.getAppByLocalId(principal.appId);
DOMApplicationRegistry.getManifestFor(app.manifestURL, function getManifest(aManifest) {
let helper = new ManifestHelper(aManifest, app.origin);
details.appName = helper.name;
browser.shell.sendChromeEvent(details);
});
details.manifestURL = DOMApplicationRegistry.getManifestURLByLocalId(principal.appId);
browser.shell.sendChromeEvent(details);
},
classID: Components.ID("{8c719f03-afe0-4aac-91ff-6c215895d467}"),

View File

@ -1,4 +1,4 @@
{
"revision": "4ab5602bc5338c921426cf44e8fbc1b2ddd69290",
"revision": "092d3aabd1ec799c748809a484009f0bdde8c51f",
"repo_path": "/integration/gaia-central"
}

View File

@ -471,6 +471,8 @@
@BINPATH@/components/NetworkInterfaceListService.js
@BINPATH@/components/TelephonyProvider.manifest
@BINPATH@/components/TelephonyProvider.js
@BINPATH@/components/NetworkStatsServiceProxy.manifest
@BINPATH@/components/NetworkStatsServiceProxy.js
#endif
#ifdef MOZ_ENABLE_DBUS
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@

View File

@ -156,10 +156,10 @@ this.webappsUI = {
}
DOMApplicationRegistry.confirmInstall(aData, localDir,
(aManifest) => {
(aManifest, aZipPath) => {
Task.spawn(function() {
try {
yield WebappsInstaller.install(aData, aManifest);
yield WebappsInstaller.install(aData, aManifest, aZipPath);
if (this.downloads[manifestURL]) {
yield this.downloads[manifestURL].promise;
}

View File

@ -9,9 +9,10 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
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/Services.jsm");
Cu.import("resource://gre/modules/WebappOSUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
return Cc["@mozilla.org/network/util;1"]
@ -187,7 +188,9 @@ this.AppsUtils = {
},
getAppInfo: function getAppInfo(aApps, aAppId) {
if (!aApps[aAppId]) {
let app = aApps[aAppId];
if (!app) {
debug("No webapp for " + aAppId);
return null;
}
@ -196,12 +199,12 @@ this.AppsUtils = {
// so we can't use the 'removable' property for isCoreApp
// Instead, we check if the app is installed under /system/b2g
let isCoreApp = false;
let app = aApps[aAppId];
#ifdef MOZ_WIDGET_GONK
isCoreApp = app.basePath == this.getCoreAppsBasePath();
#endif
debug(app.basePath + " isCoreApp: " + isCoreApp);
return { "basePath": app.basePath + "/",
return { "path": WebappOSUtils.getInstallPath(app),
"isCoreApp": isCoreApp };
},

View File

@ -1950,8 +1950,8 @@ this.DOMApplicationRegistry = {
}
// Disallow reinstalls from the same manifest URL for now.
if (this._appIdForManifestURL(app.manifestURL) !== null &&
this._isLaunchable(app)) {
let id = this._appIdForManifestURL(app.manifestURL);
if (id !== null && this._isLaunchable(this.webapps[id])) {
sendError("REINSTALL_FORBIDDEN");
return;
}
@ -2069,7 +2069,7 @@ this.DOMApplicationRegistry = {
app: app,
manifest: aManifest });
if (installSuccessCallback) {
installSuccessCallback(aManifest);
installSuccessCallback(aManifest, zipFile.path);
}
}).bind(this));
}).bind(this));
@ -2209,6 +2209,11 @@ this.DOMApplicationRegistry = {
_nextLocalId: function() {
let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
while (this.getManifestURLByLocalId(id)) {
id++;
}
Services.prefs.setIntPref("dom.mozApps.maxLocalId", id);
Services.prefs.savePrefFile(null);
return id;
@ -2938,7 +2943,8 @@ this.DOMApplicationRegistry = {
let tmp = [];
for (let appId in this.webapps) {
if (this.webapps[appId].manifestURL == aData.manifestURL) {
if (this.webapps[appId].manifestURL == aData.manifestURL &&
this._isLaunchable(this.webapps[appId])) {
aData.app = AppsUtils.cloneAppObject(this.webapps[appId]);
tmp.push({ id: appId });
break;

View File

@ -26,6 +26,7 @@ if CONFIG['MOZ_B2G_RIL']:
'nsIDOMNetworkStats.idl',
'nsIDOMNetworkStatsManager.idl',
'nsIMobileConnectionProvider.idl',
'nsINetworkStatsServiceProxy.idl',
]
XPIDL_MODULE = 'dom_network'

View File

@ -12,9 +12,15 @@ interface nsIDOMMozNetworkStatsData : nsISupports
readonly attribute jsval date; // Date.
};
[scriptable, builtinclass, uuid(037435a6-f563-48f3-99b3-a0106d8ba5bd)]
[scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
interface nsIDOMMozNetworkStats : nsISupports
{
/**
* Manifest URL of an application for specifying the per-app
* stats of the specified app. If null, system stats are returned.
*/
readonly attribute DOMString manifestURL;
/**
* Can be 'mobile', 'wifi' or null.
* If null, stats for both mobile and wifi are returned.

View File

@ -12,8 +12,12 @@ dictionary NetworkStatsOptions
* Connection type used to filter which network stats will be returned:
* 'mobile', 'wifi' or null.
* If null, stats for both mobile and wifi are returned.
*
* Manifest URL used to retrieve network stats per app.
* If null, system stats (regardless of the app) are returned.
*/
DOMString connectionType;
DOMString manifestURL;
jsval start; // date
jsval end; // date
};
@ -22,11 +26,13 @@ dictionary NetworkStatsOptions
interface nsIDOMMozNetworkStatsManager : nsISupports
{
/**
* Query network interface statistics.
* Query network statistics.
*
* If options.connectionType is not provided, return statistics for all known
* network interfaces.
*
* If options.manifestURL is not provided, return statistics regardless of the app.
*
* If successful, the request result will be an nsIDOMMozNetworkStats object.
*
* If network stats are not available for some dates, then rxBytes &

View File

@ -0,0 +1,35 @@
/* 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(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
interface nsINetworkStatsServiceProxyCallback : nsISupports
{
/*
* @param aResult callback result with boolean value
* @param aMessage message
*/
void notify(in boolean aResult, in jsval aMessage);
};
[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)]
interface nsINetworkStatsServiceProxy : nsISupports
{
/*
* An interface used to record per-app traffic data.
* @param aAppId app id
* @param aConnectionType network connection type (0 for wifi, 1 for mobile)
* @param aTimeStamp time stamp
* @param aRxBytes received data amount
* @param aTxBytes transmitted data amount
* @param aCallback an optional callback
*/
void saveAppStats(in unsigned long aAppId,
in long aConnectionType,
in unsigned long long aTimeStamp,
in unsigned long long aRxBytes,
in unsigned long long aTxBytes,
[optional] in nsINetworkStatsServiceProxyCallback aCallback);
};

View File

@ -15,8 +15,9 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
const DB_NAME = "net_stats";
const DB_VERSION = 1;
const STORE_NAME = "net_stats";
const DB_VERSION = 2;
const STORE_NAME = "net_stats"; // Deprecated. Use "net_stats_v2" instead.
const STORE_NAME_V2 = "net_stats_v2";
// Constant defining the maximum values allowed per interface. If more, older
// will be erased.
@ -30,7 +31,7 @@ this.NetworkStatsDB = function NetworkStatsDB(aGlobal, aConnectionTypes) {
debug("Constructor");
}
this._connectionTypes = aConnectionTypes;
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME], aGlobal);
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2], aGlobal);
}
NetworkStatsDB.prototype = {
@ -43,7 +44,7 @@ NetworkStatsDB.prototype = {
function errorCb(error) {
txnCb(error, null);
}
return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
return this.newTxn(txn_type, STORE_NAME_V2, callback, successCb, errorCb);
},
upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
@ -89,14 +90,52 @@ NetworkStatsDB.prototype = {
if (DEBUG) {
debug("Database initialized");
}
} else if (currVersion == 1) {
// In order to support per-app traffic data storage, the original
// objectStore needs to be replaced by a new objectStore with new
// key path ("appId") and new index ("appId").
let newObjectStore;
newObjectStore = db.createObjectStore(STORE_NAME_V2, { keyPath: ["appId", "connectionType", "timestamp"] });
newObjectStore.createIndex("appId", "appId", { unique: false });
newObjectStore.createIndex("connectionType", "connectionType", { unique: false });
newObjectStore.createIndex("timestamp", "timestamp", { unique: false });
newObjectStore.createIndex("rxBytes", "rxBytes", { unique: false });
newObjectStore.createIndex("txBytes", "txBytes", { unique: false });
newObjectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
newObjectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
if (DEBUG) {
debug("Created new object stores and indexes");
}
// Copy the data from the original objectStore to the new objectStore.
objectStore = aTransaction.objectStore(STORE_NAME);
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (!cursor) {
// Delete the original object store.
db.deleteObjectStore(STORE_NAME);
return;
}
let oldStats = cursor.value;
let newStats = { appId: 0,
connectionType: oldStats.connectionType,
timestamp: oldStats.timestamp,
rxBytes: oldStats.rxBytes,
txBytes: oldStats.txBytes,
rxTotalBytes: oldStats.rxTotalBytes,
txTotalBytes: oldStats.txTotalBytes };
this._saveStats(aTransaction, newObjectStore, newStats);
cursor.continue();
}.bind(this);
}
}
},
normalizeDate: function normalizeDate(aDate) {
normalizeDate: function normalizeDate(aDate) {
// Convert to UTC according to timezone and
// filter timestamp to get SAMPLE_RATE precission
let timestamp = aDate.getTime() - (new Date()).getTimezoneOffset() * 60 * 1000;
let timestamp = aDate.getTime() - aDate.getTimezoneOffset() * 60 * 1000;
timestamp = Math.floor(timestamp / SAMPLE_RATE) * SAMPLE_RATE;
return timestamp;
},
@ -104,12 +143,13 @@ NetworkStatsDB.prototype = {
saveStats: function saveStats(stats, aResultCb) {
let timestamp = this.normalizeDate(stats.date);
stats = {connectionType: stats.connectionType,
timestamp: timestamp,
rxBytes: 0,
txBytes: 0,
rxTotalBytes: stats.rxBytes,
txTotalBytes: stats.txBytes};
stats = { appId: stats.appId,
connectionType: stats.connectionType,
timestamp: timestamp,
rxBytes: (stats.appId == 0) ? 0 : stats.rxBytes,
txBytes: (stats.appId == 0) ? 0 : stats.txBytes,
rxTotalBytes: (stats.appId == 0) ? stats.rxBytes : 0,
txTotalBytes: (stats.appId == 0) ? stats.txBytes : 0 };
this.dbNewTxn("readwrite", function(txn, store) {
if (DEBUG) {
@ -126,13 +166,18 @@ NetworkStatsDB.prototype = {
return;
}
if (stats.appId != cursor.value.appId) {
cursor.continue();
return;
}
// There are old samples
if (DEBUG) {
debug("Last value " + JSON.stringify(cursor.value));
}
// Remove stats previous to now - VALUE_MAX_LENGTH
this._removeOldStats(txn, store, stats.connectionType, stats.timestamp);
this._removeOldStats(txn, store, stats.appId, stats.connectionType, stats.timestamp);
// Process stats before save
this._processSamplesDiff(txn, store, cursor, stats);
@ -160,17 +205,32 @@ NetworkStatsDB.prototype = {
debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff);
}
let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
if (rxDiff < 0 || txDiff < 0) {
rxDiff = newSample.rxTotalBytes;
txDiff = newSample.txTotalBytes;
// If the incoming data is obtained from netd (|newSample.appId| is 0),
// the new |txBytes|/|rxBytes| is assigend by the differnce between the new
// |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
// Else, the incoming data is per-app data (|newSample.appId| is not 0),
// the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
if (newSample.appId == 0) {
let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
if (rxDiff < 0 || txDiff < 0) {
rxDiff = newSample.rxTotalBytes;
txDiff = newSample.txTotalBytes;
}
newSample.rxBytes = rxDiff;
newSample.txBytes = txDiff;
}
newSample.rxBytes = rxDiff;
newSample.txBytes = txDiff;
if (diff == 1) {
// New element.
// If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
// needs to be obtained by adding new |rxBytes|/|txBytes| to last
// |rxTotalBytes|/|txTotalBytes|.
if (newSample.appId != 0) {
newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes;
newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes;
}
this._saveStats(txn, store, newSample);
return;
}
@ -185,7 +245,8 @@ NetworkStatsDB.prototype = {
let data = [];
for (let i = diff - 2; i >= 0; i--) {
let time = newSample.timestamp - SAMPLE_RATE * (i + 1);
let sample = {connectionType: newSample.connectionType,
let sample = {appId: newSample.appId,
connectionType: newSample.connectionType,
timestamp: time,
rxBytes: 0,
txBytes: 0,
@ -205,10 +266,21 @@ NetworkStatsDB.prototype = {
// If diff < 0, clock or timezone changed back. Place data in the last sample.
lastSample.rxBytes += rxDiff;
lastSample.txBytes += txDiff;
lastSample.rxTotalBytes = newSample.rxTotalBytes;
lastSample.txTotalBytes = newSample.txTotalBytes;
lastSample.rxBytes += newSample.rxBytes;
lastSample.txBytes += newSample.txBytes;
// If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
// needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
if (newSample.appId == 0) {
lastSample.rxTotalBytes = newSample.rxTotalBytes;
lastSample.txTotalBytes = newSample.txTotalBytes;
} else {
// Else, the incoming data is per-app data, old |rxTotalBytes|/
// |txTotalBytes| needs to get updated by adding the new
// |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
lastSample.rxTotalBytes += newSample.rxBytes;
lastSample.txTotalBytes += newSample.txBytes;
}
if (DEBUG) {
debug("Update: " + JSON.stringify(lastSample));
}
@ -231,12 +303,12 @@ NetworkStatsDB.prototype = {
}
},
_removeOldStats: function _removeOldStats(txn, store, connType, date) {
_removeOldStats: function _removeOldStats(txn, store, appId, connType, date) {
// Callback function to remove old items when new ones are added.
let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
let lowFilter = [connType, 0];
let upFilter = [connType, filterDate];
let range = this.dbGlobal.IDBKeyRange.bound(lowFilter, upFilter, false, false);
let lowerFilter = [appId, connType, 0];
let upperFilter = [appId, connType, filterDate];
let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
store.openCursor(range).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
@ -261,15 +333,16 @@ NetworkStatsDB.prototype = {
let end = this.normalizeDate(aOptions.end);
if (DEBUG) {
debug("Find: connectionType:" + aOptions.connectionType + " start: " + start + " end: " + end);
debug("Find: appId: " + aOptions.appId + " connectionType:" +
aOptions.connectionType + " start: " + start + " end: " + end);
debug("Start time: " + new Date(start));
debug("End time: " + new Date(end));
}
this.dbNewTxn("readonly", function(txn, store) {
let lowFilter = [aOptions.connectionType, start];
let upFilter = [aOptions.connectionType, end];
let range = this.dbGlobal.IDBKeyRange.bound(lowFilter, upFilter, false, false);
let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
let upperFilter = [aOptions.appId, aOptions.connectionType, end];
let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
let data = [];
@ -291,6 +364,7 @@ NetworkStatsDB.prototype = {
// now - VALUES_MAX_LENGTH, fill with empty samples.
this.fillResultSamples(start + offset, end + offset, data);
txn.result.manifestURL = aOptions.manifestURL;
txn.result.connectionType = aOptions.connectionType;
txn.result.start = aOptions.start;
txn.result.end = aOptions.end;
@ -305,14 +379,15 @@ NetworkStatsDB.prototype = {
let end = this.normalizeDate(aOptions.end);
if (DEBUG) {
debug("FindAll: start: " + start + " end: " + end + "\n");
debug("FindAll: appId: " + aOptions.appId +
" start: " + start + " end: " + end + "\n");
}
let self = this;
this.dbNewTxn("readonly", function(txn, store) {
let lowFilter = start;
let upFilter = end;
let range = this.dbGlobal.IDBKeyRange.bound(lowFilter, upFilter, false, false);
let lowerFilter = start;
let upperFilter = end;
let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
let data = [];
@ -323,6 +398,11 @@ NetworkStatsDB.prototype = {
let request = store.index("timestamp").openCursor(range).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
if (cursor.value.appId != aOptions.appId) {
cursor.continue();
return;
}
if (data.length > 0 &&
data[data.length - 1].date.getTime() == cursor.value.timestamp + offset) {
// Time is the same, so add values.
@ -339,6 +419,7 @@ NetworkStatsDB.prototype = {
this.fillResultSamples(start + offset, end + offset, data);
txn.result.manifestURL = aOptions.manifestURL;
txn.result.connectionType = aOptions.connectionType;
txn.result.start = aOptions.start;
txn.result.end = aOptions.end;

View File

@ -57,13 +57,14 @@ NetworkStatsData.prototype = {
// NetworkStats
const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
const NETWORKSTATS_CID = Components.ID("{037435a6-f563-48f3-99b3-a0106d8ba5bd}");
const NETWORKSTATS_CID = Components.ID("{6613ea55-b99c-44f9-91bf-d07da10b9b74}");
const nsIDOMMozNetworkStats = Components.interfaces.nsIDOMMozNetworkStats;
function NetworkStats(aWindow, aStats) {
if (DEBUG) {
debug("NetworkStats Constructor");
}
this.manifestURL = aStats.manifestURL || null;
this.connectionType = aStats.connectionType || null;
this.start = aStats.start || null;
this.end = aStats.end || null;
@ -76,6 +77,7 @@ function NetworkStats(aWindow, aStats) {
NetworkStats.prototype = {
__exposedProps__: {
manifestURL: 'r',
connectionType: 'r',
start: 'r',
end: 'r',

View File

@ -1,8 +1,8 @@
component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
component {037435a6-f563-48f3-99b3-a0106d8ba5bd} NetworkStatsManager.js
contract @mozilla.org/networkStats;1 {037435a6-f563-48f3-99b3-a0106d8ba5bd}
component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js
contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74}
component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js
contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6}

View File

@ -23,6 +23,9 @@ const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
// The maximum traffic amount can be saved in the |cachedAppStats|.
const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
"@mozilla.org/dom/indexeddb/manager;1",
"nsIIndexedDatabaseManager");
@ -35,6 +38,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "networkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
"@mozilla.org/AppsService;1",
"nsIAppsService");
let myGlobal = this;
this.NetworkStatsService = {
@ -74,6 +81,10 @@ this.NetworkStatsService = {
this.timer.initWithCallback(this, this._db.sampleRate,
Ci.nsITimer.TYPE_REPEATING_PRECISE);
// App stats are firstly stored in the cached.
this.cachedAppStats = Object.create(null);
this.cachedAppStatsDate = new Date();
this.updateQueue = [];
this.isQueueRunning = false;
},
@ -164,15 +175,38 @@ this.NetworkStatsService = {
* In order to return updated stats, first is performed a call to
* updateAllStats function, which will get last stats from netd
* and update the database.
* Then, depending on the request (stats per interface or total stats)
* Then, depending on the request (stats per appId or total stats)
* it retrieve them from database and return to the manager.
*/
getStats: function getStats(mm, msg) {
this.updateAllStats(function onStatsUpdated(aResult, aMessage) {
let options = msg.data;
let data = msg.data;
let options = { appId: 0,
connectionType: data.connectionType,
start: data.start,
end: data.end };
let manifestURL = data.manifestURL;
if (manifestURL) {
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
if (DEBUG) {
debug("get appId: " + appId + " from manifestURL: " + manifestURL);
}
if (!appId) {
mm.sendAsyncMessage("NetworkStats:Get:Return",
{ id: msg.id, error: "Invalid manifestURL", result: null });
return;
}
options.appId = appId;
options.manifestURL = manifestURL;
}
if (DEBUG) {
debug("getstats for: - " + options.connectionType + " -");
debug("getStats for options: " + JSON.stringify(options));
}
if (!options.connectionType || options.connectionType.length == 0) {
@ -207,6 +241,9 @@ this.NetworkStatsService = {
},
updateAllStats: function updateAllStats(callback) {
// Update |cachedAppStats|.
this.updateCachedAppStats();
let elements = [];
let lastElement;
@ -356,10 +393,11 @@ this.NetworkStatsService = {
return;
}
let stats = { connectionType: this._connectionTypes[connType].name,
let stats = { appId: 0,
connectionType: this._connectionTypes[connType].name,
date: date,
rxBytes: txBytes,
txBytes: rxBytes};
rxBytes: rxBytes,
txBytes: txBytes };
if (DEBUG) {
debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes +
@ -377,6 +415,130 @@ this.NetworkStatsService = {
});
},
/*
* Function responsible for receiving per-app stats.
*/
saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
if (DEBUG) {
debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
aTimeStamp + " " + aRxBytes + " " + aTxBytes);
}
// |aAppId| can not be 0 or null in this case.
if (!aAppId) {
return;
}
let stats = { appId: aAppId,
connectionType: this._connectionTypes[aConnectionType].name,
date: new Date(aTimeStamp),
rxBytes: aRxBytes,
txBytes: aTxBytes };
// Generate an unique key from |appId| and |connectionType|,
// which is used to retrieve data in |cachedAppStats|.
let key = stats.appId + stats.connectionType;
// |cachedAppStats| only keeps the data with the same date.
// If the incoming date is different from |cachedAppStatsDate|,
// both |cachedAppStats| and |cachedAppStatsDate| will get updated.
let diff = (this._db.normalizeDate(stats.date) -
this._db.normalizeDate(this.cachedAppStatsDate)) /
this._db.sampleRate;
if (diff != 0) {
this.updateCachedAppStats(function onUpdated(success, message) {
this.cachedAppStatsDate = stats.date;
this.cachedAppStats[key] = stats;
if (!aCallback) {
return;
}
if (!success) {
aCallback.notify(false, message);
return;
}
aCallback.notify(true, "ok");
}.bind(this));
return;
}
// Try to find the matched row in the cached by |appId| and |connectionType|.
// If not found, save the incoming data into the cached.
let appStats = this.cachedAppStats[key];
if (!appStats) {
this.cachedAppStats[key] = stats;
return;
}
// Find matched row, accumulate the traffic amount.
appStats.rxBytes += stats.rxBytes;
appStats.txBytes += stats.txBytes;
// If new rxBytes or txBytes exceeds MAX_CACHED_TRAFFIC
// the corresponding row will be saved to indexedDB.
// Then, the row will be removed from the cached.
if (appStats.rxBytes > MAX_CACHED_TRAFFIC ||
appStats.txBytes > MAX_CACHED_TRAFFIC) {
this._db.saveStats(appStats,
function (error, result) {
if (DEBUG) {
debug("Application stats inserted in indexedDB");
}
}
);
delete this.cachedAppStats[key];
}
},
updateCachedAppStats: function updateCachedAppStats(callback) {
if (DEBUG) {
debug("updateCachedAppStats: " + this.cachedAppStatsDate);
}
let stats = Object.keys(this.cachedAppStats);
if (stats.length == 0) {
// |cachedAppStats| is empty, no need to update.
return;
}
let index = 0;
this._db.saveStats(this.cachedAppStats[stats[index]],
function onSavedStats(error, result) {
if (DEBUG) {
debug("Application stats inserted in indexedDB");
}
// Clean up the |cachedAppStats| after updating.
if (index == stats.length - 1) {
this.cachedAppStats = Object.create(null);
if (!callback) {
return;
}
if (error) {
callback(false, error);
return;
}
callback(true, "ok");
return;
}
// Update is not finished, keep updating.
index += 1;
this._db.saveStats(this.cachedAppStats[stats[index]],
onSavedStats.bind(this, error, result));
}.bind(this));
},
get maxCachedTraffic () {
return MAX_CACHED_TRAFFIC;
},
logAllRecords: function logAllRecords() {
this._db.logAllRecords(function onResult(error, result) {
if (error) {

View File

@ -0,0 +1,47 @@
/* 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 DEBUG = false;
function debug(s) { dump("-*- NetworkStatsServiceProxy: " + s + "\n"); }
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
this.EXPORTED_SYMBOLS = ["NetworkStatsServiceProxy"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
const NETWORKSTATSSERVICEPROXY_CONTRACTID = "@mozilla.org/networkstatsServiceProxy;1";
const NETWORKSTATSSERVICEPROXY_CID = Components.ID("8fbd115d-f590-474c-96dc-e2b6803ca975");
const nsINetworkStatsServiceProxy = Ci.nsINetworkStatsServiceProxy;
function NetworkStatsServiceProxy() {
if (DEBUG) {
debug("Proxy started");
}
}
NetworkStatsServiceProxy.prototype = {
/*
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
* to pass the per-app stats to NetworkStatsService.
*/
saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp,
aRxBytes, aTxBytes, aCallback) {
if (DEBUG) {
debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
aTimeStamp + " " + aRxBytes + " " + aTxBytes);
}
NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp,
aRxBytes, aTxBytes, aCallback);
},
classID : NETWORKSTATSSERVICEPROXY_CID,
QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);

View File

@ -0,0 +1,2 @@
component {8fbd115d-f590-474c-96dc-e2b6803ca975} NetworkStatsServiceProxy.js
contract @mozilla.org/networkstatsServiceProxy;1 {8fbd115d-f590-474c-96dc-e2b6803ca975}

View File

@ -41,6 +41,8 @@ if CONFIG['MOZ_B2G_RIL']:
EXTRA_COMPONENTS += [
'NetworkStatsManager.js',
'NetworkStatsManager.manifest',
'NetworkStatsServiceProxy.js',
'NetworkStatsServiceProxy.manifest',
]
IPDL_SOURCES += [

View File

@ -143,7 +143,7 @@ var steps = [
}, 1000);
},
function () {
ok(true, "Get stats for a connectionType and dates adapted to samplerate");
ok(true, "Get system stats for a connectionType and dates adapted to samplerate");
// Prepare get params
var type = netStats.connectionTypes[0];
var diff = 2;
@ -161,7 +161,8 @@ var steps = [
// Launch request
req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
req.onsuccess = function () {
ok(true, "Get stats request ok");
ok(true, "Get system stats request ok");
ok(req.result.manifestURL == null, "manifestURL should be null");
ok(req.result.connectionType == type, "connectionTypes should be equals");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
@ -172,11 +173,11 @@ var steps = [
next();
};
req.onerror = function () {
ok(false, "Get stats for a connectionType failure!");
ok(false, "Get system stats for a connectionType failure!");
}
},
function () {
ok(true, "Get stats for all connectionTypes and dates adapted to samplerate");
ok(true, "Get system stats for all connectionTypes and dates adapted to samplerate");
// Prepare get params
var diff = 2;
// Get samplerate in millis
@ -194,6 +195,7 @@ var steps = [
req = netStats.getNetworkStats({start: startDate, end: endDate});
req.onsuccess = function () {
ok(true, "Get stats request ok");
ok(req.result.manifestURL == null, "manifestURL should be null");
ok(req.result.connectionType == null, "connectionTypes should be null");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
@ -204,11 +206,85 @@ var steps = [
next();
};
req.onerror = function () {
ok(false, "Get stats for all connectionTypes failure!");
ok(false, "Get system stats for all connectionTypes failure!");
}
},
function () {
ok(true, "Get stats for a connectionType and dates not adapted to samplerate");
ok(true, "Get app stats for a connectionType and dates adapted to samplerate");
// Prepare get params
var url = 'app://browser.gaiamobile.org/manifest.webapp';
var type = netStats.connectionTypes[0];
var diff = 2;
// Get samplerate in millis
var sampleRate = netStats.sampleRate * 1000;
// Get date with samplerate's precision
var offset = new Date().getTimezoneOffset() * 60 * 1000;
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
* sampleRate + offset);
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
// Calculate the number of samples that should be returned based on the
// the samplerate and including final and initial samples.
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
// Launch request
req = netStats.getNetworkStats({start: startDate,
end: endDate,
connectionType: type,
manifestURL: url});
req.onsuccess = function () {
ok(true, "Get app stats request ok");
ok(req.result.manifestURL == url, "manifestURL should be equals");
ok(req.result.connectionType == type, "connectionTypes should be equals");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
var data = req.result.data;
ok(Array.isArray(data) && data.length == samples,
"data is an array of length " + samples);
checkDataDates(data, startDate, endDate, sampleRate);
next();
};
req.onerror = function () {
ok(false, "Get app stats for a connectionType failure!");
}
},
function () {
ok(true, "Get app stats for all connectionTypes and dates adapted to samplerate");
// Prepare get params
var url = 'app://browser.gaiamobile.org/manifest.webapp';
var diff = 2;
// Get samplerate in millis
var sampleRate = netStats.sampleRate * 1000;
// Get date with samplerate's precision
var offset = new Date().getTimezoneOffset() * 60 * 1000;
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
* sampleRate + offset);
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
// Calculate the number of samples that should be returned based on the
// the samplerate and including final and initial samples.
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
// Launch request
req = netStats.getNetworkStats({start: startDate,
end: endDate,
manifestURL: url});
req.onsuccess = function () {
ok(true, "Get app stats request ok");
ok(req.result.manifestURL == url, "manifestURL should be equals");
ok(req.result.connectionType == null, "connectionTypes should be null");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
var data = req.result.data;
ok(Array.isArray(data) && data.length == samples,
"data is an array of length " + samples);
checkDataDates(data, startDate, endDate, sampleRate);
next();
};
req.onerror = function () {
ok(false, "Get app stats for all connectionTypes failure!");
}
},
function () {
ok(true, "Get system stats for a connectionType and dates not adapted to samplerate");
// Prepare get params
var type = netStats.connectionTypes[0];
var diff = 2;
@ -225,7 +301,8 @@ var steps = [
// Launch request
req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
req.onsuccess = function () {
ok(true, "Get stats request ok");
ok(true, "Get system stats request ok");
ok(req.result.manifestURL == null, "manifestURL should be null");
ok(req.result.connectionType == type, "connectionTypes should be equals");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
@ -236,11 +313,11 @@ var steps = [
next();
};
req.onerror = function () {
ok(false, "Get stats for a connectionType failure!");
ok(false, "Get system stats for a connectionType failure!");
}
},
function () {
ok(true, "Get stats for all connectionTypes and dates not adapted to samplerate");
ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate");
// Prepare get params
var diff = 2;
// Get samplerate in millis
@ -258,6 +335,7 @@ var steps = [
req = netStats.getNetworkStats({start: startDate, end: endDate});
req.onsuccess = function () {
ok(true, "Get stats request ok");
ok(req.result.manifestURL == null, "manifestURL should be null");
ok(req.result.connectionType == null, "connectionTypes should be null");
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
@ -268,7 +346,7 @@ var steps = [
next();
};
req.onerror = function () {
ok(false, "Get stats for all connectionType failure!");
ok(false, "Get system stats for all connectionType failure!");
}
},
function () {

View File

@ -1,4 +1,4 @@
/* Any copyright is dedicated to the Public Domain.
/* Any: copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
@ -96,7 +96,8 @@ add_test(function test_clear() {
});
add_test(function test_internalSaveStats_singleSample() {
var stats = {connectionType: "wifi",
var stats = {appId: 0,
connectionType: "wifi",
timestamp: Date.now(),
rxBytes: 0,
txBytes: 0,
@ -111,6 +112,7 @@ add_test(function test_internalSaveStats_singleSample() {
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_eq(result[0].connectionType, stats.connectionType);
do_check_eq(result[0].timestamp, stats.timestamp);
do_check_eq(result[0].rxBytes, stats.rxBytes);
@ -129,7 +131,8 @@ add_test(function test_internalSaveStats_arraySamples() {
var samples = 2;
var stats = [];
for (var i = 0; i < samples; i++) {
stats.push({connectionType: "wifi",
stats.push({appId: 0,
connectionType: "wifi",
timestamp: Date.now() + (10 * i),
rxBytes: 0,
txBytes: 0,
@ -148,7 +151,8 @@ add_test(function test_internalSaveStats_arraySamples() {
var success = true;
for (var i = 0; i < samples; i++) {
if (result[i].connectionType != stats[i].connectionType ||
if (result[i].appId != stats[i].appId ||
result[i].connectionType != stats[i].connectionType ||
result[i].timestamp != stats[i].timestamp ||
result[i].rxBytes != stats[i].rxBytes ||
result[i].txBytes != stats[i].txBytes ||
@ -172,20 +176,22 @@ add_test(function test_internalRemoveOldStats() {
var samples = 10;
var stats = [];
for (var i = 0; i < samples - 1; i++) {
stats.push({connectionType: "wifi", timestamp: Date.now() + (10 * i),
stats.push({appId: 0,
connectionType: "wifi", timestamp: Date.now() + (10 * i),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234});
}
stats.push({connectionType: "wifi", timestamp: Date.now() + (10 * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234});
stats.push({appId: 0,
connectionType: "wifi", timestamp: Date.now() + (10 * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234});
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, stats);
var date = stats[stats.length -1].timestamp
+ (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
netStatsDb._removeOldStats(txn, store, "wifi", date);
netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
}, function(error, result) {
do_check_eq(error, null);
@ -226,16 +232,19 @@ function processSamplesDiff(lastStat, newStat, callback) {
add_test(function test_processSamplesDiffSameSample() {
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = {connectionType: "wifi", timestamp: date,
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {connectionType: "wifi", timestamp: date,
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234};
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, newStat.appId);
do_check_eq(result[0].connectionType, newStat.connectionType);
do_check_eq(result[0].timestamp, newStat.timestamp);
do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
@ -249,16 +258,19 @@ add_test(function test_processSamplesDiffSameSample() {
add_test(function test_processSamplesDiffNextSample() {
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = {connectionType: "wifi", timestamp: date,
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {connectionType: "wifi", timestamp: date + sampleRate,
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date + sampleRate,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 500, txTotalBytes: 500};
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, 2);
do_check_eq(result[1].appId, newStat.appId);
do_check_eq(result[1].connectionType, newStat.connectionType);
do_check_eq(result[1].timestamp, newStat.timestamp);
do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
@ -273,16 +285,19 @@ add_test(function test_processSamplesDiffSamplesLost() {
var samples = 5;
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = {connectionType: "wifi", timestamp: date,
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {connectionType: "wifi", timestamp: date + (sampleRate * samples),
rxBytes: 0, txBytes: 0,
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date + (sampleRate * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234};
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, samples + 1);
do_check_eq(result[0].appId, newStat.appId);
do_check_eq(result[samples].connectionType, newStat.connectionType);
do_check_eq(result[samples].timestamp, newStat.timestamp);
do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
@ -294,10 +309,11 @@ add_test(function test_processSamplesDiffSamplesLost() {
});
add_test(function test_saveStats() {
var stats = { connectionType: "wifi",
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
var stats = {appId: 0,
connectionType: "wifi",
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
@ -306,6 +322,7 @@ add_test(function test_saveStats() {
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_eq(result[0].connectionType, stats.connectionType);
let timestamp = filterTimestamp(stats.date);
do_check_eq(result[0].timestamp, timestamp);
@ -319,6 +336,34 @@ add_test(function test_saveStats() {
});
});
add_test(function test_saveAppStats() {
var stats = {appId: 1,
connectionType: "wifi",
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.saveStats(stats, function(error, result) {
do_check_eq(error, null);
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_eq(result[0].connectionType, stats.connectionType);
let timestamp = filterTimestamp(stats.date);
do_check_eq(result[0].timestamp, timestamp);
do_check_eq(result[0].rxBytes, stats.rxBytes);
do_check_eq(result[0].txBytes, stats.txBytes);
do_check_eq(result[0].rxTotalBytes, 0);
do_check_eq(result[0].txTotalBytes, 0);
run_next_test();
});
});
});
});
function prepareFind(stats, callback) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
@ -338,14 +383,16 @@ add_test(function test_find () {
var end = new Date(start + (sampleRate * (samples - 1)));
start = new Date(start - sampleRate);
var stats = [];
for (var i = 0; i < samples; i++) {i
stats.push({connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
for (var i = 0; i < samples; i++) {
stats.push({appId: 0,
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
stats.push({connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
stats.push({appId: 0,
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
}
prepareFind(stats, function(error, result) {
@ -371,8 +418,103 @@ add_test(function test_find () {
do_check_eq(result.data[1].txBytes, 20);
do_check_eq(result.data[samples].rxBytes, 0);
run_next_test();
}, {start: start, end: end});
}, {start: start, end: end, connectionType: "wifi"});
}, {appId: 0, start: start, end: end});
}, {start: start, end: end, connectionType: "wifi", appId: 0});
});
});
add_test(function test_findAppStats () {
var samples = 5;
var sampleRate = netStatsDb.sampleRate;
var start = Date.now();
var saveDate = filterTimestamp(new Date());
var end = new Date(start + (sampleRate * (samples - 1)));
start = new Date(start - sampleRate);
var stats = [];
for (var i = 0; i < samples; i++) {
stats.push({appId: 1,
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
stats.push({appId: 1,
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
}
prepareFind(stats, function(error, result) {
do_check_eq(error, null);
netStatsDb.find(function (error, result) {
do_check_eq(error, null);
do_check_eq(result.connectionType, "wifi");
do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[samples].rxBytes, 0);
netStatsDb.findAll(function (error, result) {
do_check_eq(error, null);
do_check_eq(result.connectionType, null);
do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[1].txBytes, 20);
do_check_eq(result.data[samples].rxBytes, 0);
run_next_test();
}, {start: start, end: end, appId: 1});
}, {start: start, end: end, connectionType: "wifi", appId: 1});
});
});
add_test(function test_saveMultipleAppStats () {
var saveDate = filterTimestamp(new Date());
var cached = Object.create(null);
cached['1wifi'] = {appId: 1,
connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['1mobile'] = {appId: 1,
connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['2wifi'] = {appId: 2,
connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['2mobile'] = {appId: 2,
connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10};
let keys = Object.keys(cached);
let index = 0;
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.saveStats(cached[keys[index]],
function callback(error, result) {
do_check_eq(error, null);
if (index == keys.length - 1) {
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
do_check_eq(result.length, 4);
do_check_eq(result[0].appId, 1);
do_check_eq(result[0].connectionType, 'mobile');
do_check_eq(result[0].rxBytes, 0);
do_check_eq(result[0].txBytes, 10);
run_next_test();
});
}
index += 1;
netStatsDb.saveStats(cached[keys[index]], callback);
});
});
});

View File

@ -0,0 +1,119 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
"@mozilla.org/networkstatsServiceProxy;1",
"nsINetworkStatsServiceProxy");
add_test(function test_saveAppStats() {
var cachedAppStats = NetworkStatsService.cachedAppStats;
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var samples = 5;
do_check_eq(Object.keys(cachedAppStats).length, 0);
for (var i = 0; i < samples; i++) {
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
timestamp, 10, 20);
}
var key1 = 1 + 'wifi';
var key2 = 1 + 'mobile';
do_check_eq(Object.keys(cachedAppStats).length, 2);
do_check_eq(cachedAppStats[key1].appId, 1);
do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key1].rxBytes, 50);
do_check_eq(cachedAppStats[key1].txBytes, 100);
do_check_eq(cachedAppStats[key2].appId, 1);
do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key2].rxBytes, 50);
do_check_eq(cachedAppStats[key2].txBytes, 100);
run_next_test();
});
add_test(function test_saveAppStatsWithDifferentDates() {
var today = NetworkStatsService.cachedAppStatsDate;
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
var key = 1 + 'wifi';
NetworkStatsService.updateCachedAppStats(
function (success, msg) {
do_check_eq(success, true);
do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
today.getTime(), 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
today.getTime(), 10, 20);
var saveAppStatsCb = {
notify: function notify(success, message) {
do_check_eq(success, true);
var cachedAppStats = NetworkStatsService.cachedAppStats;
var key = 2 + 'mobile';
do_check_eq(Object.keys(cachedAppStats).length, 1);
do_check_eq(cachedAppStats[key].appId, 2);
do_check_eq(cachedAppStats[key].connectionType, 'mobile');
do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
Math.floor(tomorrow.getTime() / 1000));
do_check_eq(cachedAppStats[key].rxBytes, 30);
do_check_eq(cachedAppStats[key].txBytes, 40);
run_next_test();
}
};
nssProxy.saveAppStats(2, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
tomorrow.getTime(), 30, 40, saveAppStatsCb);
}
);
});
add_test(function test_saveAppStatsWithMaxCachedTraffic() {
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var maxtraffic = NetworkStatsService.maxCachedTraffic;
NetworkStatsService.updateCachedAppStats(
function (success, msg) {
do_check_eq(success, true);
var cachedAppStats = NetworkStatsService.cachedAppStats;
do_check_eq(Object.keys(cachedAppStats).length, 0);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, 10, 20);
do_check_eq(Object.keys(cachedAppStats).length, 1);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, maxtraffic, 20);
do_check_eq(Object.keys(cachedAppStats).length, 0);
run_next_test();
});
});
function run_test() {
do_get_profile();
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
run_next_test();
}

View File

@ -3,4 +3,5 @@ head =
tail =
[test_networkstats_service.js]
[test_networkstats_service_proxy.js]
[test_networkstats_db.js]

View File

@ -481,6 +481,8 @@ pref("devtools.debugger.force-local", true);
pref("devtools.debugger.prompt-connection", true);
// Temporary setting to enable webapps actors
pref("devtools.debugger.enable-content-actors", true);
// Block tools from seeing / interacting with certified apps
pref("devtools.debugger.forbid-certified-apps", true);
// view source
pref("view_source.syntax_highlight", true);

View File

@ -70,10 +70,10 @@ AppProtocolHandler.prototype = {
let uri;
if (this._runningInParent || appInfo.isCoreApp) {
// In-parent and CoreApps can directly access files, so use jar:file://
uri = "jar:file://" + appInfo.basePath + appId + "/application.zip!" + fileSpec;
uri = "jar:file://" + appInfo.path + "/application.zip!" + fileSpec;
} else {
// non-CoreApps in child need to ask parent for file handle, use jar:ipcfile://
uri = "jar:remoteopenfile://" + appInfo.basePath + appId + "/application.zip!" + fileSpec;
uri = "jar:remoteopenfile://" + appInfo.path + "/application.zip!" + fileSpec;
}
let channel = Services.io.newChannel(uri, null, null);
channel.QueryInterface(Ci.nsIJARChannel).setAppURI(aURI);

View File

@ -3,6 +3,7 @@ head = head_apps.js
tail = tail_apps.js
[test_webappsActor.js]
skip-if = (os == "win" || "linux" || "mac")
[test_appInstall.js]
# Persistent failures.
skip-if = true

View File

@ -500,13 +500,36 @@ WebappsActor.prototype = {
let defer = promise.defer();
let reg = DOMApplicationRegistry;
reg.getAll(function onsuccess(apps) {
defer.resolve({ apps: apps });
reg.getAll(apps => {
defer.resolve({ apps: this._filterAllowedApps(apps) });
});
return defer.promise;
},
_areCertifiedAppsAllowed: function wa__areCertifiedAppsAllowed() {
let pref = "devtools.debugger.forbid-certified-apps";
return !Services.prefs.getBoolPref(pref);
},
_isAppAllowedForManifest: function wa__isAppAllowedForManifest(aManifest) {
if (this._areCertifiedAppsAllowed()) {
return true;
}
let type = this._getAppType(aManifest.type);
return type !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
},
_filterAllowedApps: function wa__filterAllowedApps(aApps) {
return aApps.filter(app => this._isAppAllowedForManifest(app.manifest));
},
_isAppAllowedForURL: function wa__isAppAllowedForURL(aManifestURL) {
return this._findManifestByURL(aManifestURL).then(manifest => {
return this._isAppAllowedForManifest(manifest);
});
},
uninstall: function wa_actorUninstall(aRequest) {
debug("uninstall");
@ -531,6 +554,19 @@ WebappsActor.prototype = {
return defer.promise;
},
_findManifestByURL: function wa__findManifestByURL(aManifestURL) {
let deferred = promise.defer();
let reg = DOMApplicationRegistry;
let id = reg._appIdForManifestURL(aManifestURL);
reg._readManifests([{ id: id }], function (aResults) {
deferred.resolve(aResults[0].manifest);
});
return deferred.promise;
},
getIconAsDataURL: function (aRequest) {
debug("getIconAsDataURL");
@ -549,9 +585,7 @@ WebappsActor.prototype = {
let deferred = promise.defer();
let id = reg._appIdForManifestURL(manifestURL);
reg._readManifests([{ id: id }], function (aResults) {
let jsonManifest = aResults[0].manifest;
this._findManifestByURL(manifestURL).then(jsonManifest => {
let manifest = new ManifestHelper(jsonManifest, app.origin);
let iconURL = manifest.iconURLForSize(aRequest.size || 128);
if (!iconURL) {
@ -665,14 +699,22 @@ WebappsActor.prototype = {
listRunningApps: function (aRequest) {
debug("listRunningApps\n");
let appPromises = [];
let apps = [];
for each (let frame in this._appFrames()) {
let manifestURL = frame.getAttribute("mozapp");
apps.push(manifestURL);
appPromises.push(this._isAppAllowedForURL(manifestURL).then(allowed => {
if (allowed) {
apps.push(manifestURL);
}
}));
}
return { apps: apps };
return promise.all(appPromises).then(() => {
return { apps: apps };
});
},
_connectToApp: function (aFrame) {
@ -745,24 +787,34 @@ WebappsActor.prototype = {
}
}
let notFoundError = {
error: "appNotFound",
message: "Unable to find any opened app whose manifest " +
"is '" + manifestURL + "'"
};
if (!appFrame) {
return { error: "appNotFound",
message: "Unable to find any opened app whose manifest " +
"is '" + manifestURL + "'" };
return notFoundError;
}
// Only create a new actor, if we haven't already
// instanciated one for this connection.
let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager;
let actor = this._appActorsMap.get(mm);
if (!actor) {
return this._connectToApp(appFrame)
.then(function (actor) ({ actor: actor }));
}
return this._isAppAllowedForURL(manifestURL).then(allowed => {
if (!allowed) {
return notFoundError;
}
return { actor: actor };
// Only create a new actor, if we haven't already
// instanciated one for this connection.
let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager;
let actor = this._appActorsMap.get(mm);
if (!actor) {
return this._connectToApp(appFrame)
.then(function (actor) ({ actor: actor }));
}
return { actor: actor };
});
},
watchApps: function () {
@ -800,19 +852,30 @@ WebappsActor.prototype = {
}
this._openedApps.add(manifestURL);
this.conn.send({ from: this.actorID,
type: "appOpen",
manifestURL: manifestURL
});
this._isAppAllowedForURL(manifestURL).then(allowed => {
if (allowed) {
this.conn.send({ from: this.actorID,
type: "appOpen",
manifestURL: manifestURL
});
}
});
break;
case "appterminated":
manifestURL = event.detail.manifestURL;
this._openedApps.delete(manifestURL);
this.conn.send({ from: this.actorID,
type: "appClose",
manifestURL: manifestURL
});
this._isAppAllowedForURL(manifestURL).then(allowed => {
if (allowed) {
this.conn.send({ from: this.actorID,
type: "appClose",
manifestURL: manifestURL
});
}
});
break;
}
}

View File

@ -8,31 +8,38 @@ const CC = Components.Constructor;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
this.EXPORTED_SYMBOLS = ["WebappOSUtils"];
// Returns the MD5 hash of a string.
function computeHash(aString) {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let result = {};
// Data is an array of bytes.
let data = converter.convertToByteArray(aString, result);
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
hasher.init(hasher.MD5);
hasher.update(data, data.length);
// We're passing false to get the binary hash and not base64.
let hash = hasher.finish(false);
function toHexString(charCode) {
return ("0" + charCode.toString(16)).slice(-2);
}
// Convert the binary hash data to a hex string.
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
}
this.WebappOSUtils = {
getUniqueName: function(aApp) {
let name;
// During the installation of a new app, the aApp object
// doesn't have a name property. We then need to use the manifest.
// For some mozApps calls, the aApp object doesn't have a manifest
// associated, and so we need to use the name property.
// They're guaranteed to be always identical to the application
// name in the user locale.
if (aApp.name) {
name = aApp.name;
} else {
let manifest =
new ManifestHelper(aApp.updateManifest || aApp.manifest, aApp.origin);
name = manifest.name;
}
return this.sanitizeStringForFilename(name).toLowerCase() + "-" +
AppsUtils.computeHash(aApp.manifestURL);
return this.sanitizeStringForFilename(aApp.name).toLowerCase() + "-" +
computeHash(aApp.manifestURL);
},
/**
@ -87,15 +94,20 @@ this.WebappOSUtils = {
.createInstance(Ci.nsIMacWebAppUtils);
try {
if (mwaUtils.pathForAppWithIdentifier(uniqueName)) {
return uniqueName;
}
if (mwaUtils.pathForAppWithIdentifier(aApp.origin)) {
return aApp.origin;
let path;
if (path = mwaUtils.pathForAppWithIdentifier(uniqueName)) {
return [ uniqueName, path ];
}
} catch(ex) {}
return null;
try {
let path;
if (path = mwaUtils.pathForAppWithIdentifier(aApp.origin)) {
return [ aApp.origin, path ];
}
} catch(ex) {}
return [ null, null ];
#elifdef XP_UNIX
let exeFile = Services.dirsvc.get("Home", Ci.nsIFile);
exeFile.append("." + uniqueName);
@ -121,6 +133,31 @@ this.WebappOSUtils = {
#endif
},
getInstallPath: function(aApp) {
#ifdef XP_WIN
let execFile = this.getLaunchTarget(aApp);
if (!execFile) {
return null;
}
return execFile.parent.path;
#elifdef XP_MACOSX
let [ bundleID, path ] = this.getLaunchTarget(aApp);
return path;
#elifdef MOZ_B2G
return aApp.basePath + "/" + aApp.id;
#elifdef MOZ_FENNEC
return aApp.basePath + "/" + aApp.id;
#elifdef XP_UNIX
let execFile = this.getLaunchTarget(aApp);
if (!execFile) {
return null;
}
return execFile.parent.path;
#endif
},
launch: function(aApp) {
let uniqueName = this.getUniqueName(aApp);
@ -142,7 +179,7 @@ this.WebappOSUtils = {
return true;
#elifdef XP_MACOSX
let launchIdentifier = this.getLaunchTarget(aApp);
let [ launchIdentifier, path ] = this.getLaunchTarget(aApp);
if (!launchIdentifier) {
return false;
}
@ -215,7 +252,7 @@ this.WebappOSUtils = {
return true;
#elifdef XP_MACOSX
if (!this.getLaunchTarget(aApp)) {
if (!this.getInstallPath(aApp)) {
return false;
}

View File

@ -61,8 +61,10 @@ this.WebappsInstaller = {
*
* @param aData the data provided to the install function
* @param aManifest the manifest data provided by the web app
* @param aZipPath path to the zip file for packaged apps (undefined for
* hosted apps)
*/
install: function(aData, aManifest) {
install: function(aData, aManifest, aZipPath) {
try {
if (Services.prefs.getBoolPref("browser.mozApps.installer.dry_run")) {
return Promise.resolve();
@ -71,7 +73,7 @@ this.WebappsInstaller = {
this.shell.init(aData, aManifest);
return this.shell.install().then(() => {
return this.shell.install(aZipPath).then(() => {
let data = {
"installDir": this.shell.installDir.path,
"app": {
@ -96,11 +98,12 @@ this.WebappsInstaller = {
*
*/
function NativeApp(aData) {
this.uniqueName = WebappOSUtils.getUniqueName(aData.app);
let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
aData.app.name = manifest.name;
this.uniqueName = WebappOSUtils.getUniqueName(aData.app);
this.appName = sanitize(manifest.name);
this.appNameAsFilename = stripStringForFilename(this.appName);
}
@ -126,10 +129,11 @@ NativeApp.prototype = {
*
*/
init: function(aData, aManifest) {
let app = aData.app;
let manifest = this.manifest = new ManifestHelper(aManifest,
aData.app.origin);
app.origin);
let origin = Services.io.newURI(aData.app.origin, null, null);
let origin = Services.io.newURI(app.origin, null, null);
let biggestIcon = getBiggestIconURL(manifest.icons);
try {
@ -169,7 +173,7 @@ NativeApp.prototype = {
this.shortDescription = this.appName;
}
this.categories = aData.app.categories.slice(0);
this.categories = app.categories.slice(0);
// The app registry is the Firefox profile from which the app
// was installed.
@ -179,11 +183,27 @@ NativeApp.prototype = {
"registryDir": registryFolder.path,
"app": {
"manifest": aManifest,
"origin": aData.app.origin,
"manifestURL": aData.app.manifestURL
"origin": app.origin,
"manifestURL": app.manifestURL,
"installOrigin": app.installOrigin,
"categories": app.categories,
"receipts": app.receipts,
"installTime": app.installTime,
}
};
if (app.etag) {
this.webappJson.app.etag = app.etag;
}
if (app.packageEtag) {
this.webappJson.app.packageEtag = app.packageEtag;
}
if (app.updateManifest) {
this.webappJson.app.updateManifest = app.updateManifest;
}
this.runtimeFolder = Services.dirsvc.get("GreD", Ci.nsIFile);
},
@ -193,6 +213,23 @@ NativeApp.prototype = {
*/
getIcon: function() {
try {
// If the icon is in the zip package, we should modify the url
// to point to the zip file (we can't use the app protocol yet
// because the app isn't installed yet).
if (this.iconURI.scheme == "app") {
let zipFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsIFile);
zipFile.initWithPath(OS.Path.join(this.installDir.path,
"application.zip"));
let zipUrl = Services.io.newFileURI(zipFile).spec;
let filePath = this.iconURI.QueryInterface(Ci.nsIURL).filePath;
this.iconURI = Services.io.newURI("jar:" + zipUrl + "!" + filePath,
null, null);
}
let [ mimeType, icon ] = yield downloadIcon(this.iconURI);
yield this.processIcon(mimeType, icon);
}
@ -271,13 +308,19 @@ WinNativeApp.prototype = {
* Install the app in the system
*
*/
install: function() {
install: function(aZipPath) {
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createShortcutFiles();
this._createConfigFiles();
this._writeSystemKeys();
if (aZipPath) {
yield OS.File.move(aZipPath, OS.Path.join(this.installDir.path,
"application.zip"));
}
yield this.getIcon();
} catch (ex) {
this._removeInstallation(false);
@ -624,11 +667,17 @@ MacNativeApp.prototype = {
this._createDirectoryStructure();
},
install: function() {
install: function(aZipPath) {
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
if (aZipPath) {
yield OS.File.move(aZipPath, OS.Path.join(this.installDir.path,
"application.zip"));
}
yield this.getIcon();
this._moveToApplicationsFolder();
} catch (ex) {
@ -821,11 +870,17 @@ LinuxNativeApp.prototype = {
this._createDirectoryStructure();
},
install: function() {
install: function(aZipPath) {
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
if (aZipPath) {
yield OS.File.move(aZipPath, OS.Path.join(this.installDir.path,
"application.zip"));
}
yield this.getIcon();
} catch (ex) {
this._removeInstallation(false);

View File

@ -29,6 +29,9 @@
#include <EGL/egl.h>
#include <hardware/hardware.h>
#if ANDROID_VERSION == 17
#include <gui/SurfaceTextureClient.h>
#endif
#include <ui/GraphicBuffer.h>
#include "FramebufferSurface.h"

View File

@ -14,7 +14,11 @@
*/
#include "GonkDisplayJB.h"
#if ANDROID_VERSION == 17
#include <gui/SurfaceTextureClient.h>
#else
#include <gui/Surface.h>
#endif
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
@ -46,10 +50,10 @@ GonkDisplayJB::GonkDisplayJB()
ALOGW_IF(err, "could not open framebuffer");
}
if (!err) {
if (!err && mFBDevice) {
mWidth = mFBDevice->width;
mHeight = mFBDevice->height;
xdpi = mFBDevice->xdpi;
mHeight = mFBDevice->height;
xdpi = mFBDevice->xdpi;
/* The emulator actually reports RGBA_8888, but EGL doesn't return
* any matching configuration. We force RGBX here to fix it. */
surfaceformat = HAL_PIXEL_FORMAT_RGBX_8888;
@ -99,7 +103,11 @@ GonkDisplayJB::GonkDisplayJB()
mAlloc = new GraphicBufferAlloc();
mFBSurface = new FramebufferSurface(0, mWidth, mHeight, surfaceformat, mAlloc);
#if ANDROID_VERSION == 17
sp<SurfaceTextureClient> stc = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >(mFBSurface->getBufferQueue()));
#else
sp<Surface> stc = new Surface(static_cast<sp<IGraphicBufferProducer> >(mFBSurface->getBufferQueue()));
#endif
mSTClient = stc;
mList = (hwc_display_contents_1_t *)malloc(sizeof(*mList) + (sizeof(hwc_layer_1_t)*2));
@ -108,7 +116,10 @@ GonkDisplayJB::GonkDisplayJB()
status_t error;
mBootAnimBuffer = mAlloc->createGraphicBuffer(mWidth, mHeight, surfaceformat, GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER, &error);
StartBootAnimation();
if (error == NO_ERROR && mBootAnimBuffer.get())
StartBootAnimation();
else
ALOGW("Couldn't show bootanimation (%s)", strerror(-error));
}
GonkDisplayJB::~GonkDisplayJB()
@ -173,8 +184,14 @@ GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur)
if (mFBDevice && mFBDevice->compositionComplete) {
mFBDevice->compositionComplete(mFBDevice);
}
#if ANDROID_VERSION == 17
mList->dpy = dpy;
mList->sur = sur;
#else
mList->outbuf = nullptr;
mList->outbufAcquireFenceFd = -1;
#endif
eglSwapBuffers(dpy, sur);
return Post(mFBSurface->lastHandle, mFBSurface->lastFenceFD);
}
@ -213,7 +230,9 @@ GonkDisplayJB::Post(buffer_handle_t buf, int fence)
mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].sourceCrop;
mList->hwLayers[1].acquireFenceFd = fence;
mList->hwLayers[1].releaseFenceFd = -1;
#if ANDROID_VERSION == 18
mList->hwLayers[1].planeAlpha = 0xFF;
#endif
mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
mFBSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);

View File

@ -20,7 +20,7 @@ CPP_SOURCES += [
'BootAnimation.cpp',
]
if CONFIG['ANDROID_VERSION'] == '18':
if CONFIG['ANDROID_VERSION'] >= '17':
CPP_SOURCES += [
'FramebufferSurface.cpp',
'GraphicBufferAlloc.cpp',