author | Ryan VanderMeulen <ryanvm@gmail.com> |
Wed, 16 Oct 2013 17:12:54 -0400 | |
changeset 151030 | 423b9c30c73d12176db4bd80d38206eaa5032767 |
parent 151029 | 66af8b3096a61f6eb95059817eb0de629d0c187b |
child 151041 | 9ab5b1d2d2b82f34ca783ce345ca1d8d3801b283 |
child 155878 | 0ca23e59862d727b1c6bdec7339d546960fd0cb3 |
child 161467 | 4dda9a646aea5bc02383ad6c2a0322637a2c4843 |
push id | 25475 |
push user | ryanvm@gmail.com |
push date | Wed, 16 Oct 2013 21:12:54 +0000 |
treeherder | autoland@423b9c30c73d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 887699 |
milestone | 27.0a1 |
backs out | 5c878c48d7320232a7ecbe79354b2a1e8ece5576 57b03d7055e86388d330d8af204df4d68c886506 ea06175feb4f7069750bdac30bb73a9645098533 516d0f14f7fd5c1ae20e76e259d322f1841288a7 |
first release with | nightly linux32
423b9c30c73d
/
27.0a1
/
20131017030201
/
files
nightly linux64
423b9c30c73d
/
27.0a1
/
20131017030201
/
files
nightly mac
423b9c30c73d
/
27.0a1
/
20131017030201
/
files
nightly win32
423b9c30c73d
/
27.0a1
/
20131017030201
/
files
nightly win64
423b9c30c73d
/
27.0a1
/
20131017030201
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
27.0a1
/
20131017030201
/
pushlog to previous
nightly linux64
27.0a1
/
20131017030201
/
pushlog to previous
nightly mac
27.0a1
/
20131017030201
/
pushlog to previous
nightly win32
27.0a1
/
20131017030201
/
pushlog to previous
nightly win64
27.0a1
/
20131017030201
/
pushlog to previous
|
--- a/dom/network/interfaces/nsIDOMNetworkStats.idl +++ b/dom/network/interfaces/nsIDOMNetworkStats.idl @@ -1,40 +1,39 @@ /* 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 nsIDOMMozNetworkStatsInterface; - [scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)] interface nsIDOMMozNetworkStatsData : nsISupports { readonly attribute unsigned long rxBytes; // Received bytes. readonly attribute unsigned long txBytes; // Sent bytes. readonly attribute jsval date; // Date. }; -[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)] +[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; /** - * Network the returned data belongs to. + * Can be 'mobile', 'wifi' or null. + * If null, stats for both mobile and wifi are returned. */ - readonly attribute nsIDOMMozNetworkStatsInterface network; + readonly attribute DOMString connectionType; /** - * Stats for a network. + * Stats for connectionType */ readonly attribute jsval data; // array of NetworkStatsData. // one element per day. /** * Dates */ readonly attribute jsval start; // Date.
--- a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl +++ b/dom/network/interfaces/nsIDOMNetworkStatsManager.idl @@ -1,70 +1,62 @@ /* 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 nsIDOMDOMRequest; -/** - * Represents a data interface for which the manager is recording statistics. - */ -[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)] -interface nsIDOMMozNetworkStatsInterface : nsISupports +dictionary NetworkStatsOptions { - readonly attribute long type; - /** - * Id value is '0' for wifi or the iccid for mobile (SIM). + * 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. */ - readonly attribute DOMString id; + DOMString connectionType; + DOMString manifestURL; + jsval start; // date + jsval end; // date }; -[scriptable, uuid(5fbdcae6-a2cd-47b3-929f-83ac75bd4881)] +[scriptable, uuid(87529a6c-aef6-11e1-a595-4f034275cfa6)] interface nsIDOMMozNetworkStatsManager : nsISupports { /** - * Constants for known interface types. - */ - const long WIFI = 0; - const long MOBILE = 1; - - /** - * Find samples between two dates start and end, both included. + * 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 manifestURL is provided, per-app usage is retrieved, - * otherwise the target will be system usage. + * If successful, the request result will be an nsIDOMMozNetworkStats object. * - * If success, the request result will be an nsIDOMMozNetworkStats object. + * If network stats are not available for some dates, then rxBytes & + * txBytes are undefined for those dates. */ - nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network, - in jsval start, - in jsval end, - [optional] in DOMString manifestURL); + nsIDOMDOMRequest getNetworkStats(in jsval options); /** - * Remove all stats related with the provided network from DB. + * Return available connection types. */ - nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network); + readonly attribute jsval connectionTypes; // array of DOMStrings. /** - * Remove all stats in the database. + * Clear all stats from DB. */ - nsIDOMDOMRequest clearAllStats(); + nsIDOMDOMRequest clearAllData(); /** - * Return currently available networks. + * Time in seconds between samples stored in database. */ - readonly attribute jsval availableNetworks; // array of nsIDOMMozNetworkStatsInterface. + readonly attribute long sampleRate; /** - * Minimum time in milliseconds between samples stored in the database. + * Maximum number of samples stored in the database per connection type. */ - readonly attribute long sampleRate; - - /** - * Time in milliseconds recorded by the API until present time. All samples - * older than maxStorageAge from now are deleted. - */ - readonly attribute long long maxStorageAge; + readonly attribute long maxStorageSamples; };
--- a/dom/network/interfaces/nsINetworkStatsServiceProxy.idl +++ b/dom/network/interfaces/nsINetworkStatsServiceProxy.idl @@ -1,37 +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" -interface nsINetworkInterface; - [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(facef032-3fd9-4509-a396-83d94c1a11ae)] +[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)] interface nsINetworkStatsServiceProxy : nsISupports { /* * An interface used to record per-app traffic data. * @param aAppId app id - * @param aNetworkInterface network + * @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 nsINetworkInterface aNetwork, + in long aConnectionType, in unsigned long long aTimeStamp, in unsigned long long aRxBytes, in unsigned long long aTxBytes, [optional] in nsINetworkStatsServiceProxyCallback aCallback); };
--- a/dom/network/src/NetworkStatsDB.jsm +++ b/dom/network/src/NetworkStatsDB.jsm @@ -11,43 +11,45 @@ function debug(s) { dump("-*- NetworkSta const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); const DB_NAME = "net_stats"; const DB_VERSION = 2; -const STORE_NAME = "net_stats"; +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. const VALUES_MAX_LENGTH = 6 * 30; // Constant defining the rate of the samples. Daily. const SAMPLE_RATE = 1000 * 60 * 60 * 24; -this.NetworkStatsDB = function NetworkStatsDB() { +this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) { if (DEBUG) { debug("Constructor"); } - this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]); + this._connectionTypes = aConnectionTypes; + this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]); } NetworkStatsDB.prototype = { __proto__: IndexedDBHelper.prototype, dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) { function successCb(result) { txnCb(null, result); } 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) { if (DEBUG) { debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!"); } let db = aDb; let objectStore; @@ -62,392 +64,371 @@ NetworkStatsDB.prototype = { objectStore.createIndex("timestamp", "timestamp", { unique: false }); objectStore.createIndex("rxBytes", "rxBytes", { unique: false }); objectStore.createIndex("txBytes", "txBytes", { unique: false }); objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false }); objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false }); if (DEBUG) { debug("Created object stores and indexes"); } + + // There could be a time delay between the point when the network + // interface comes up and the point when the database is initialized. + // In this short interval some traffic data are generated but are not + // registered by the first sample. The initialization of the database + // should make up the missing sample. + let stats = []; + for (let connection in this._connectionTypes) { + let connectionType = this._connectionTypes[connection].name; + let timestamp = this.normalizeDate(new Date()); + stats.push({ connectionType: connectionType, + timestamp: timestamp, + rxBytes: 0, + txBytes: 0, + rxTotalBytes: 0, + txTotalBytes: 0 }); + } + this._saveStats(aTransaction, objectStore, stats); + 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"). - // Also, since now networks are identified by their - // [networkId, networkType] not just by their connectionType, - // to modify the keyPath is mandatory to delete the object store - // and create it again. Old data is going to be deleted because the - // networkId for each sample can not be set. - db.deleteObjectStore(STORE_NAME); + 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"); + } - objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] }); - objectStore.createIndex("appId", "appId", { unique: false }); - objectStore.createIndex("network", "network", { unique: false }); - objectStore.createIndex("networkType", "networkType", { unique: false }); - objectStore.createIndex("timestamp", "timestamp", { unique: false }); - objectStore.createIndex("rxBytes", "rxBytes", { unique: false }); - objectStore.createIndex("txBytes", "txBytes", { unique: false }); - objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false }); - objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false }); + // 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; + } - debug("Created object stores and indexes for version 2"); + 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); } } }, - importData: function importData(aStats) { - let stats = { appId: aStats.appId, - network: [aStats.networkId, aStats.networkType], - timestamp: aStats.timestamp, - rxBytes: aStats.rxBytes, - txBytes: aStats.txBytes, - rxTotalBytes: aStats.rxTotalBytes, - txTotalBytes: aStats.txTotalBytes }; - - return stats; - }, - - exportData: function exportData(aStats) { - let stats = { appId: aStats.appId, - networkId: aStats.network[0], - networkType: aStats.network[1], - timestamp: aStats.timestamp, - rxBytes: aStats.rxBytes, - txBytes: aStats.txBytes, - rxTotalBytes: aStats.rxTotalBytes, - txTotalBytes: aStats.txTotalBytes }; - - return stats; - }, - normalizeDate: function normalizeDate(aDate) { // Convert to UTC according to timezone and // filter timestamp to get SAMPLE_RATE precission let timestamp = aDate.getTime() - aDate.getTimezoneOffset() * 60 * 1000; timestamp = Math.floor(timestamp / SAMPLE_RATE) * SAMPLE_RATE; return timestamp; }, - saveStats: function saveStats(aStats, aResultCb) { - let timestamp = this.normalizeDate(aStats.date); + saveStats: function saveStats(stats, aResultCb) { + let timestamp = this.normalizeDate(stats.date); - let stats = { appId: aStats.appId, - networkId: aStats.networkId, - networkType: aStats.networkType, - timestamp: timestamp, - rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes, - txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes, - rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0, - txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 }; + 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 }; - stats = this.importData(stats); - - this.dbNewTxn("readwrite", function(aTxn, aStore) { + this.dbNewTxn("readwrite", function(txn, store) { if (DEBUG) { debug("Filtered time: " + new Date(timestamp)); debug("New stats: " + JSON.stringify(stats)); } - let request = aStore.index("network").openCursor(stats.network, "prev"); + let request = store.index("connectionType").openCursor(stats.connectionType, "prev"); request.onsuccess = function onsuccess(event) { let cursor = event.target.result; if (!cursor) { // Empty, so save first element. - - // There could be a time delay between the point when the network - // interface comes up and the point when the database is initialized. - // In this short interval some traffic data are generated but are not - // registered by the first sample. - if (stats.appId == 0) { - stats.rxBytes = stats.rxTotalBytes; - stats.txBytes = stats.txTotalBytes; - } - - this._saveStats(aTxn, aStore, stats); + this._saveStats(txn, store, stats); 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(aTxn, aStore, stats.appId, stats.network, stats.timestamp); + this._removeOldStats(txn, store, stats.appId, stats.connectionType, stats.timestamp); // Process stats before save - this._processSamplesDiff(aTxn, aStore, cursor, stats); + this._processSamplesDiff(txn, store, cursor, stats); }.bind(this); }.bind(this), aResultCb); }, /* * This function check that stats are saved in the database following the sample rate. * In this way is easier to find elements when stats are requested. */ - _processSamplesDiff: function _processSamplesDiff(aTxn, aStore, aLastSampleCursor, aNewSample) { - let lastSample = aLastSampleCursor.value; + _processSamplesDiff: function _processSamplesDiff(txn, store, lastSampleCursor, newSample) { + let lastSample = lastSampleCursor.value; // Get difference between last and new sample. - let diff = (aNewSample.timestamp - lastSample.timestamp) / SAMPLE_RATE; + let diff = (newSample.timestamp - lastSample.timestamp) / SAMPLE_RATE; if (diff % 1) { // diff is decimal, so some error happened because samples are stored as a multiple // of SAMPLE_RATE - aTxn.abort(); + txn.abort(); throw new Error("Error processing samples"); } if (DEBUG) { - debug("New: " + aNewSample.timestamp + " - Last: " + - lastSample.timestamp + " - diff: " + diff); + debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff); } // 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 (aNewSample.appId == 0) { - let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes; - let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes; + if (newSample.appId == 0) { + let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes; + let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes; if (rxDiff < 0 || txDiff < 0) { - rxDiff = aNewSample.rxTotalBytes; - txDiff = aNewSample.txTotalBytes; + rxDiff = newSample.rxTotalBytes; + txDiff = newSample.txTotalBytes; } - aNewSample.rxBytes = rxDiff; - aNewSample.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 (aNewSample.appId != 0) { - aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes; - aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes; + if (newSample.appId != 0) { + newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes; + newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes; } - - this._saveStats(aTxn, aStore, aNewSample); + this._saveStats(txn, store, newSample); return; } if (diff > 1) { // Some samples lost. Device off during one or more samplerate periods. // Time or timezone changed // Add lost samples with 0 bytes and the actual one. if (diff > VALUES_MAX_LENGTH) { diff = VALUES_MAX_LENGTH; } let data = []; for (let i = diff - 2; i >= 0; i--) { - let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1); - let sample = { appId: aNewSample.appId, - network: aNewSample.network, - timestamp: time, - rxBytes: 0, - txBytes: 0, - rxTotalBytes: lastSample.rxTotalBytes, - txTotalBytes: lastSample.txTotalBytes }; - + let time = newSample.timestamp - SAMPLE_RATE * (i + 1); + let sample = {appId: newSample.appId, + connectionType: newSample.connectionType, + timestamp: time, + rxBytes: 0, + txBytes: 0, + rxTotalBytes: lastSample.rxTotalBytes, + txTotalBytes: lastSample.txTotalBytes}; data.push(sample); } - data.push(aNewSample); - this._saveStats(aTxn, aStore, data); + data.push(newSample); + this._saveStats(txn, store, data); return; } if (diff == 0 || diff < 0) { // New element received before samplerate period. // It means that device has been restarted (or clock / timezone change). // Update element. // If diff < 0, clock or timezone changed back. Place data in the last sample. - lastSample.rxBytes += aNewSample.rxBytes; - lastSample.txBytes += aNewSample.txBytes; + 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 (aNewSample.appId == 0) { - lastSample.rxTotalBytes = aNewSample.rxTotalBytes; - lastSample.txTotalBytes = aNewSample.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 += aNewSample.rxBytes; - lastSample.txTotalBytes += aNewSample.txBytes; + lastSample.rxTotalBytes += newSample.rxBytes; + lastSample.txTotalBytes += newSample.txBytes; } if (DEBUG) { debug("Update: " + JSON.stringify(lastSample)); } - let req = aLastSampleCursor.update(lastSample); + let req = lastSampleCursor.update(lastSample); } }, - _saveStats: function _saveStats(aTxn, aStore, aNetworkStats) { + _saveStats: function _saveStats(txn, store, networkStats) { if (DEBUG) { - debug("_saveStats: " + JSON.stringify(aNetworkStats)); + debug("_saveStats: " + JSON.stringify(networkStats)); } - if (Array.isArray(aNetworkStats)) { - let len = aNetworkStats.length - 1; + if (Array.isArray(networkStats)) { + let len = networkStats.length - 1; for (let i = 0; i <= len; i++) { - aStore.put(aNetworkStats[i]); + store.put(networkStats[i]); } } else { - aStore.put(aNetworkStats); + store.put(networkStats); } }, - _removeOldStats: function _removeOldStats(aTxn, aStore, aAppId, aNetwork, aDate) { + _removeOldStats: function _removeOldStats(txn, store, appId, connType, date) { // Callback function to remove old items when new ones are added. - let filterDate = aDate - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1); - let lowerFilter = [aAppId, aNetwork, 0]; - let upperFilter = [aAppId, aNetwork, filterDate]; + let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1); + let lowerFilter = [appId, connType, 0]; + let upperFilter = [appId, connType, filterDate]; let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false); - let lastSample = null; - let self = this; - - aStore.openCursor(range).onsuccess = function(event) { + store.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { - lastSample = cursor.value; cursor.delete(); cursor.continue(); - return; } - - // If all samples for a network are removed, an empty sample - // has to be saved to keep the totalBytes in order to compute - // future samples because system counters are not set to 0. - // Thus, if there are no samples left, the last sample removed - // will be saved again after setting its bytes to 0. - let request = aStore.index("network").openCursor(aNetwork); - request.onsuccess = function onsuccess(event) { - let cursor = event.target.result; - if (!cursor && lastSample != null) { - let timestamp = new Date(); - timestamp = self.normalizeDate(timestamp); - lastSample.timestamp = timestamp; - lastSample.rxBytes = 0; - lastSample.txBytes = 0; - self._saveStats(aTxn, aStore, lastSample); - } - }; - }; + }.bind(this); }, - clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) { - let network = [aNetwork.id, aNetwork.type]; - let self = this; - - // Clear and save an empty sample to keep sync with system counters - this.dbNewTxn("readwrite", function(aTxn, aStore) { - let sample = null; - let request = aStore.index("network").openCursor(network, "prev"); - request.onsuccess = function onsuccess(event) { - let cursor = event.target.result; - if (cursor) { - if (!sample) { - sample = cursor.value; - } - - cursor.delete(); - cursor.continue(); - return; - } - - if (sample) { - let timestamp = new Date(); - timestamp = self.normalizeDate(timestamp); - sample.timestamp = timestamp; - sample.appId = 0; - sample.rxBytes = 0; - sample.txBytes = 0; - - self._saveStats(aTxn, aStore, sample); - } - }; + clear: function clear(aResultCb) { + this.dbNewTxn("readwrite", function(txn, store) { + if (DEBUG) { + debug("Going to clear all!"); + } + store.clear(); }, aResultCb); }, - clearStats: function clearStats(aNetworks, aResultCb) { - let index = 0; - let stats = []; - let self = this; - - let callback = function(aError, aResult) { - index++; - - if (!aError && index < aNetworks.length) { - self.clearInterfaceStats(aNetworks[index], callback); - return; - } - - aResultCb(aError, aResult); - }; - - if (!aNetworks[index]) { - aResultCb(null, true); - return; - } - this.clearInterfaceStats(aNetworks[index], callback); - }, - - find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) { + find: function find(aResultCb, aOptions) { let offset = (new Date()).getTimezoneOffset() * 60 * 1000; - let start = this.normalizeDate(aStart); - let end = this.normalizeDate(aEnd); + let start = this.normalizeDate(aOptions.start); + let end = this.normalizeDate(aOptions.end); if (DEBUG) { - debug("Find samples for appId: " + aAppId + " network " + - JSON.stringify(aNetwork) + " from " + start + " until " + 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(aTxn, aStore) { - let network = [aNetwork.id, aNetwork.type]; - let lowerFilter = [aAppId, network, start]; - let upperFilter = [aAppId, network, end]; + this.dbNewTxn("readonly", function(txn, store) { + let lowerFilter = [aOptions.appId, aOptions.connectionType, start]; + let upperFilter = [aOptions.appId, aOptions.connectionType, end]; let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false); let data = []; - if (!aTxn.result) { - aTxn.result = {}; + if (!txn.result) { + txn.result = {}; } - let request = aStore.openCursor(range).onsuccess = function(event) { + let request = store.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor){ data.push({ rxBytes: cursor.value.rxBytes, txBytes: cursor.value.txBytes, date: new Date(cursor.value.timestamp + offset) }); cursor.continue(); return; } // When requested samples (start / end) are not in the range of now and // now - VALUES_MAX_LENGTH, fill with empty samples. this.fillResultSamples(start + offset, end + offset, data); - aTxn.result.manifestURL = aManifestURL; - aTxn.result.network = aNetwork; - aTxn.result.start = aStart; - aTxn.result.end = aEnd; - aTxn.result.data = data; + txn.result.manifestURL = aOptions.manifestURL; + txn.result.connectionType = aOptions.connectionType; + txn.result.start = aOptions.start; + txn.result.end = aOptions.end; + txn.result.data = data; + }.bind(this); + }.bind(this), aResultCb); + }, + + findAll: function findAll(aResultCb, aOptions) { + let offset = (new Date()).getTimezoneOffset() * 60 * 1000; + let start = this.normalizeDate(aOptions.start); + let end = this.normalizeDate(aOptions.end); + + if (DEBUG) { + debug("FindAll: appId: " + aOptions.appId + + " start: " + start + " end: " + end + "\n"); + } + + let self = this; + this.dbNewTxn("readonly", function(txn, store) { + let lowerFilter = start; + let upperFilter = end; + let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false); + + let data = []; + + if (!txn.result) { + txn.result = {}; + } + + 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. + data[data.length - 1].rxBytes += cursor.value.rxBytes; + data[data.length - 1].txBytes += cursor.value.txBytes; + } else { + data.push({ rxBytes: cursor.value.rxBytes, + txBytes: cursor.value.txBytes, + date: new Date(cursor.value.timestamp + offset) }); + } + cursor.continue(); + return; + } + + 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; + txn.result.data = data; }.bind(this); }.bind(this), aResultCb); }, /* * Fill data array (samples from database) with empty samples to match * requested start / end dates. */ @@ -475,15 +456,15 @@ NetworkStatsDB.prototype = { return SAMPLE_RATE; }, get maxStorageSamples () { return VALUES_MAX_LENGTH; }, logAllRecords: function logAllRecords(aResultCb) { - this.dbNewTxn("readonly", function(aTxn, aStore) { - aStore.mozGetAll().onsuccess = function onsuccess(event) { - aTxn.result = event.target.result; + this.dbNewTxn("readonly", function(txn, store) { + store.mozGetAll().onsuccess = function onsuccess(event) { + txn.result = event.target.result; }; }, aResultCb); }, };
--- a/dom/network/src/NetworkStatsManager.js +++ b/dom/network/src/NetworkStatsManager.js @@ -50,90 +50,60 @@ NetworkStatsData.prototype = { contractID:"@mozilla.org/networkstatsdata;1", classDescription: "NetworkStatsData", interfaces: [nsIDOMMozNetworkStatsData], flags: nsIClassInfo.DOM_OBJECT}), QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData]) }; -// NetworkStatsInterface -const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1"; -const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}"); -const nsIDOMMozNetworkStatsInterface = Components.interfaces.nsIDOMMozNetworkStatsInterface; - -function NetworkStatsInterface(aNetwork) { - if (DEBUG) { - debug("NetworkStatsInterface Constructor"); - } - this.type = aNetwork.type; - this.id = aNetwork.id; -} - -NetworkStatsInterface.prototype = { - __exposedProps__: { - id: 'r', - type: 'r', - }, - - classID : NETWORKSTATSINTERFACE_CID, - classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID, - contractID: NETWORKSTATSINTERFACE_CONTRACTID, - classDescription: "NetworkStatsInterface", - interfaces: [nsIDOMMozNetworkStatsInterface], - flags: nsIClassInfo.DOM_OBJECT}), - - QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface]) -} - // NetworkStats const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1"; -const NETWORKSTATS_CID = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}"); +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.network = new NetworkStatsInterface(aStats.network); + this.connectionType = aStats.connectionType || null; this.start = aStats.start || null; this.end = aStats.end || null; let samples = this.data = Cu.createArrayIn(aWindow); for (let i = 0; i < aStats.data.length; i++) { samples.push(new NetworkStatsData(aStats.data[i])); } } NetworkStats.prototype = { __exposedProps__: { manifestURL: 'r', - network: 'r', + connectionType: 'r', start: 'r', end: 'r', data: 'r', }, classID : NETWORKSTATS_CID, classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID, contractID: NETWORKSTATS_CONTRACTID, classDescription: "NetworkStats", interfaces: [nsIDOMMozNetworkStats], flags: nsIClassInfo.DOM_OBJECT}), QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats, - nsIDOMMozNetworkStatsData, - nsIDOMMozNetworkStatsInterface]) + nsIDOMMozNetworkStatsData]) } // NetworkStatsManager const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1"; -const NETWORKSTATSMANAGER_CID = Components.ID("{5fbdcae6-a2cd-47b3-929f-83ac75bd4881}"); +const NETWORKSTATSMANAGER_CID = Components.ID("{87529a6c-aef6-11e1-a595-4f034275cfa6}"); const nsIDOMMozNetworkStatsManager = Components.interfaces.nsIDOMMozNetworkStatsManager; function NetworkStatsManager() { if (DEBUG) { debug("Constructor"); } } @@ -141,74 +111,52 @@ NetworkStatsManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, checkPrivileges: function checkPrivileges() { if (!this.hasPrivileges) { throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE); } }, - getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) { + getNetworkStats: function getNetworkStats(aOptions) { this.checkPrivileges(); - if (aStart.constructor.name !== "Date" || - aEnd.constructor.name !== "Date" || - aStart > aEnd) { + if (!aOptions.start || !aOptions.end || + aOptions.start > aOptions.end) { throw Components.results.NS_ERROR_INVALID_ARG; } let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:Get", - { network: aNetwork, - start: aStart, - end: aEnd, - manifestURL: aManifestURL, - id: this.getRequestId(request) }); + {data: aOptions, id: this.getRequestId(request)}); return request; }, - clearStats: function clearStats(aNetwork) { + clearAllData: function clearAllData() { this.checkPrivileges(); let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:Clear", - { network: aNetwork, - id: this.getRequestId(request) }); - return request; - }, - - clearAllStats: function clearAllStats() { - this.checkPrivileges(); - - let request = this.createRequest(); - cpmm.sendAsyncMessage("NetworkStats:ClearAll", {id: this.getRequestId(request)}); return request; }, - get availableNetworks() { + get connectionTypes() { this.checkPrivileges(); - - let result = ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Networks")[0], this._window); - let networks = this.data = Cu.createArrayIn(this._window); - for (let i = 0; i < result.length; i++) { - networks.push(new NetworkStatsInterface(result[i])); - } - - return networks; + return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window); }, get sampleRate() { this.checkPrivileges(); - return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0]; + return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000; }, - get maxStorageAge() { + get maxStorageSamples() { this.checkPrivileges(); - return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0]; + return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0]; }, receiveMessage: function(aMessage) { if (DEBUG) { debug("NetworkStatsmanager::receiveMessage: " + aMessage.name); } let msg = aMessage.json; @@ -230,17 +178,16 @@ NetworkStatsManager.prototype = { let result = new NetworkStats(this._window, msg.result); if (DEBUG) { debug("result: " + JSON.stringify(result)); } Services.DOMRequest.fireSuccess(req, result); break; case "NetworkStats:Clear:Return": - case "NetworkStats:ClearAll:Return": if (msg.error) { Services.DOMRequest.fireError(req, msg.error); return; } Services.DOMRequest.fireSuccess(req, true); break; @@ -270,18 +217,17 @@ NetworkStatsManager.prototype = { debug("has privileges: " + this.hasPrivileges); } if (!this.hasPrivileges) { return null; } this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return", - "NetworkStats:Clear:Return", - "NetworkStats:ClearAll:Return"]); + "NetworkStats:Clear:Return"]); }, // Called from DOMRequestIpcHelper uninit: function uninit() { if (DEBUG) { debug("uninit call"); } }, @@ -294,11 +240,10 @@ NetworkStatsManager.prototype = { classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID, contractID: NETWORKSTATSMANAGER_CONTRACTID, classDescription: "NetworkStatsManager", interfaces: [nsIDOMMozNetworkStatsManager], flags: nsIClassInfo.DOM_OBJECT}) } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData, - NetworkStatsInterface, NetworkStats, NetworkStatsManager]);
--- a/dom/network/src/NetworkStatsManager.manifest +++ b/dom/network/src/NetworkStatsManager.manifest @@ -1,12 +1,9 @@ component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c} -component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js -contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} +component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js +contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74} -component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js -contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645} - -component {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} NetworkStatsManager.js -contract @mozilla.org/networkStatsManager;1 {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} +component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js +contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6} category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
--- a/dom/network/src/NetworkStatsService.jsm +++ b/dom/network/src/NetworkStatsService.jsm @@ -1,108 +1,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/. */ "use strict"; const DEBUG = false; -function debug(s) { - if (DEBUG) { - dump("-*- NetworkStatsService: " + s + "\n"); - } -} +function debug(s) { dump("-*- NetworkStatsService: " + s + "\n"); } const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; this.EXPORTED_SYMBOLS = ["NetworkStatsService"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetworkStatsDB.jsm"); const NET_NETWORKSTATSSERVICE_CONTRACTID = "@mozilla.org/network/netstatsservice;1"; const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471e7f6c0a4}"); const TOPIC_INTERFACE_REGISTERED = "network-interface-registered"; 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; +const NET_TYPE_UNKNOWN = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN; // The maximum traffic amount can be saved in the |cachedAppStats|. const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageListenerManager"); XPCOMUtils.defineLazyServiceGetter(this, "networkManager", "@mozilla.org/network/manager;1", "nsINetworkManager"); XPCOMUtils.defineLazyServiceGetter(this, "appsService", "@mozilla.org/AppsService;1", "nsIAppsService"); -XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService", - "@mozilla.org/settingsService;1", - "nsISettingsService"); - -XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () { - let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]); - // TODO: Bug 923382 - B2G Multi-SIM: support multiple SIM cards for network metering. - return ril.getRadioInterface(0); -}); - this.NetworkStatsService = { init: function() { - debug("Service started"); + if (DEBUG) { + debug("Service started"); + } Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false); Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false); Services.obs.addObserver(this, "profile-after-change", false); this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - // Object to store network interfaces, each network interface is composed - // by a network object (network type and network Id) and a interfaceName - // that contains the name of the physical interface (wlan0, rmnet0, etc.). - // The network type can be 0 for wifi or 1 for mobile. On the other hand, - // the network id is '0' for wifi or the iccid for mobile (SIM). - // Each networkInterface is placed in the _networks object by the index of - // 'networkId + networkType'. - // - // _networks object allows to map available network interfaces at low level - // (wlan0, rmnet0, etc.) to a network. It's not mandatory to have a - // networkInterface per network but can't exist a networkInterface not - // being mapped to a network. + this._connectionTypes = Object.create(null); + this._connectionTypes[NET_TYPE_WIFI] = { name: "wifi", + network: Object.create(null) }; + this._connectionTypes[NET_TYPE_MOBILE] = { name: "mobile", + network: Object.create(null) }; - this._networks = Object.create(null); - - // There is no way to know a priori if wifi connection is available, - // just when the wifi driver is loaded, but it is unloaded when - // wifi is switched off. So wifi connection is hardcoded - let netId = this.getNetworkId('0', NET_TYPE_WIFI); - this._networks[netId] = { network: { id: '0', - type: NET_TYPE_WIFI }, - interfaceName: null }; this.messages = ["NetworkStats:Get", "NetworkStats:Clear", - "NetworkStats:ClearAll", - "NetworkStats:Networks", + "NetworkStats:Types", "NetworkStats:SampleRate", - "NetworkStats:MaxStorageAge"]; + "NetworkStats:MaxStorageSamples"]; - this.messages.forEach(function(aMsgName) { - ppmm.addMessageListener(aMsgName, this); + this.messages.forEach(function(msgName) { + ppmm.addMessageListener(msgName, this); }, this); - this._db = new NetworkStatsDB(); + this._db = new NetworkStatsDB(this._connectionTypes); // Stats for all interfaces are updated periodically 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(); @@ -111,66 +83,68 @@ this.NetworkStatsService = { this.isQueueRunning = false; }, receiveMessage: function(aMessage) { if (!aMessage.target.assertPermission("networkstats-manage")) { return; } - debug("receiveMessage " + aMessage.name); - + if (DEBUG) { + debug("receiveMessage " + aMessage.name); + } let mm = aMessage.target; let msg = aMessage.json; switch (aMessage.name) { case "NetworkStats:Get": - this.getSamples(mm, msg); + this.getStats(mm, msg); break; case "NetworkStats:Clear": - this.clearInterfaceStats(mm, msg); - break; - case "NetworkStats:ClearAll": this.clearDB(mm, msg); break; - case "NetworkStats:Networks": - return this.availableNetworks(); + case "NetworkStats:Types": + // This message is sync. + let types = []; + for (let i in this._connectionTypes) { + types.push(this._connectionTypes[i].name); + } + return types; case "NetworkStats:SampleRate": // This message is sync. return this._db.sampleRate; - case "NetworkStats:MaxStorageAge": + case "NetworkStats:MaxStorageSamples": // This message is sync. - return this._db.maxStorageSamples * this._db.sampleRate; + return this._db.maxStorageSamples; } }, - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { + observe: function observe(subject, topic, data) { + switch (topic) { case TOPIC_INTERFACE_REGISTERED: case TOPIC_INTERFACE_UNREGISTERED: - // If new interface is registered (notified from NetworkManager), // the stats are updated for the new interface without waiting to - // complete the updating period. - - let network = aSubject.QueryInterface(Ci.nsINetworkInterface); - debug("Network " + network.name + " of type " + network.type + " status change"); - - let netId = this.convertNetworkInterface(network); - if (!netId) { - break; + // complete the updating period + let network = subject.QueryInterface(Ci.nsINetworkInterface); + if (DEBUG) { + debug("Network " + network.name + " of type " + network.type + " status change"); + } + if (this._connectionTypes[network.type]) { + this._connectionTypes[network.type].network = network; + this.updateStats(network.type); + } + break; + case "xpcom-shutdown": + if (DEBUG) { + debug("Service shutdown"); } - this.updateStats(netId); - break; - case "xpcom-shutdown": - debug("Service shutdown"); - - this.messages.forEach(function(aMsgName) { - ppmm.removeMessageListener(aMsgName, this); + this.messages.forEach(function(msgName) { + ppmm.removeMessageListener(msgName, this); }, this); Services.obs.removeObserver(this, "xpcom-shutdown"); Services.obs.removeObserver(this, "profile-after-change"); Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED); Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED); this.timer.cancel(); @@ -181,206 +155,167 @@ this.NetworkStatsService = { break; } }, /* * nsITimerCallback * Timer triggers the update of all stats */ - notify: function(aTimer) { + notify: function(timer) { this.updateAllStats(); }, /* - * nsINetworkStatsService - */ - - convertNetworkInterface: function(aNetwork) { - if (aNetwork.type != NET_TYPE_MOBILE && - aNetwork.type != NET_TYPE_WIFI) { - return null; - } - - let id = '0'; - if (aNetwork.type == NET_TYPE_MOBILE) { - // Bug 904542 will provide the serviceId to map the iccId with the - // nsINetworkInterface of the NetworkManager. Now, lets assume that - // network is mapped with the current iccId of the single SIM. - id = gRadioInterface.rilContext.iccInfo.iccid; - } - - let netId = this.getNetworkId(id, aNetwork.type); - - if (!this._networks[netId]) { - this._networks[netId] = Object.create(null); - this._networks[netId].network = { id: id, - type: aNetwork.type }; - } - - this._networks[netId].interfaceName = aNetwork.name; - return netId; - }, - - getNetworkId: function getNetworkId(aIccId, aNetworkType) { - return aIccId + '' + aNetworkType; - }, - - availableNetworks: function availableNetworks() { - let result = []; - for (let netId in this._networks) { - result.push(this._networks[netId].network); - } - - return result; - }, - - /* * Function called from manager to get stats from database. * 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 appId or total stats) * it retrieve them from database and return to the manager. */ - getSamples: function getSamples(mm, msg) { - let self = this; - let network = msg.network; - let netId = this.getNetworkId(network.id, network.type); + getStats: function getStats(mm, msg) { + this.updateAllStats(function onStatsUpdated(aResult, aMessage) { + + 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; + } - if (!this._networks[netId]) { + options.appId = appId; + options.manifestURL = manifestURL; + } + + if (DEBUG) { + debug("getStats for options: " + JSON.stringify(options)); + } + + if (!options.connectionType || options.connectionType.length == 0) { + this._db.findAll(function onStatsFound(error, result) { + mm.sendAsyncMessage("NetworkStats:Get:Return", + { id: msg.id, error: error, result: result }); + }, options); + return; + } + + for (let i in this._connectionTypes) { + if (this._connectionTypes[i].name == options.connectionType) { + this._db.find(function onStatsFound(error, result) { + mm.sendAsyncMessage("NetworkStats:Get:Return", + { id: msg.id, error: error, result: result }); + }, options); + return; + } + } + mm.sendAsyncMessage("NetworkStats:Get:Return", { id: msg.id, error: "Invalid connectionType", result: null }); - return; - } - let appId = 0; - let manifestURL = msg.manifestURL; - if (manifestURL) { - appId = appsService.getAppLocalIdByManifestURL(manifestURL); + }.bind(this)); + }, - if (!appId) { - mm.sendAsyncMessage("NetworkStats:Get:Return", - { id: msg.id, error: "Invalid manifestURL", result: null }); - return; - } - } - - let start = new Date(msg.start); - let end = new Date(msg.end); - - this.updateStats(netId, function onStatsUpdated(aResult, aMessage) { - debug("getstats for network " + network.id + " of type " + network.type); - debug("appId: " + appId + " from manifestURL: " + manifestURL); - - self._db.find(function onStatsFound(aError, aResult) { - mm.sendAsyncMessage("NetworkStats:Get:Return", - { id: msg.id, error: aError, result: aResult }); - }, network, start, end, appId, manifestURL); - + clearDB: function clearDB(mm, msg) { + this._db.clear(function onDBCleared(error, result) { + mm.sendAsyncMessage("NetworkStats:Clear:Return", + { id: msg.id, error: error, result: result }); }); }, - clearInterfaceStats: function clearInterfaceStats(mm, msg) { - let network = msg.network; - let netId = this.getNetworkId(network.id, network.type); - - debug("clear stats for network " + network.id + " of type " + network.type); - - if (!this._networks[netId]) { - mm.sendAsyncMessage("NetworkStats:Clear:Return", - { id: msg.id, error: "Invalid networkType", result: null }); - return; - } - - this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) { - mm.sendAsyncMessage("NetworkStats:Clear:Return", - { id: msg.id, error: aError, result: aResult }); - }); - }, - - clearDB: function clearDB(mm, msg) { - let networks = this.availableNetworks(); - this._db.clearStats(networks, function onDBCleared(aError, aResult) { - mm.sendAsyncMessage("NetworkStats:ClearAll:Return", - { id: msg.id, error: aError, result: aResult }); - }); - }, - - updateAllStats: function updateAllStats(aCallback) { + updateAllStats: function updateAllStats(callback) { // Update |cachedAppStats|. this.updateCachedAppStats(); let elements = []; let lastElement; // For each connectionType create an object containning the type // and the 'queueIndex', the 'queueIndex' is an integer representing // the index of a connection type in the global queue array. So, if // the connection type is already in the queue it is not appended again, // else it is pushed in 'elements' array, which later will be pushed to // the queue array. - for (let netId in this._networks) { - lastElement = { netId: netId, - queueIndex: this.updateQueueIndex(netId)}; - + for (let i in this._connectionTypes) { + lastElement = { type: i, + queueIndex: this.updateQueueIndex(i)}; if (lastElement.queueIndex == -1) { - elements.push({netId: lastElement.netId, callbacks: []}); + elements.push({type: lastElement.type, callbacks: []}); } } if (elements.length > 0) { // If length of elements is greater than 0, callback is set to // the last element. - elements[elements.length - 1].callbacks.push(aCallback); + elements[elements.length - 1].callbacks.push(callback); this.updateQueue = this.updateQueue.concat(elements); } else { // Else, it means that all connection types are already in the queue to // be updated, so callback for this request is added to // the element in the main queue with the index of the last 'lastElement'. // But before is checked that element is still in the queue because it can // be processed while generating 'elements' array. - let element = this.updateQueue[lastElement.queueIndex]; - if (aCallback && - (!element || element.netId != lastElement.netId)) { - aCallback(); + + if (!this.updateQueue[lastElement.queueIndex] || + this.updateQueue[lastElement.queueIndex].type != lastElement.queueIndex) { + if (callback) { + callback(); + } return; } - this.updateQueue[lastElement.queueIndex].callbacks.push(aCallback); + this.updateQueue[lastElement.queueIndex].callbacks.push(callback); } // Call the function that process the elements of the queue. this.processQueue(); if (DEBUG) { this.logAllRecords(); } }, - updateStats: function updateStats(aNetId, aCallback) { - // Check if the connection is in the main queue, push a new element + updateStats: function updateStats(connectionType, callback) { + // Check if the connection type is in the main queue, push a new element // if it is not being processed or add a callback if it is. - let index = this.updateQueueIndex(aNetId); + let index = this.updateQueueIndex(connectionType); if (index == -1) { - this.updateQueue.push({netId: aNetId, callbacks: [aCallback]}); + this.updateQueue.push({type: connectionType, callbacks: [callback]}); } else { - this.updateQueue[index].callbacks.push(aCallback); + this.updateQueue[index].callbacks.push(callback); } // Call the function that process the elements of the queue. this.processQueue(); }, /* - * Find if a connection is in the main queue array and return its + * Find if a connection type is in the main queue array and return its * index, if it is not in the array return -1. */ - updateQueueIndex: function updateQueueIndex(aNetId) { - return this.updateQueue.map(function(e) { return e.netId; }).indexOf(aNetId); + updateQueueIndex: function updateQueueIndex(type) { + for (let i in this.updateQueue) { + if (this.updateQueue[i].type == type) { + return i; + } + } + return -1; }, /* * Function responsible of process all requests in the queue. */ processQueue: function processQueue(aResult, aMessage) { // If aResult is not undefined, the caller of the function is the result // of processing an element, so remove that element and call the callbacks @@ -407,109 +342,101 @@ this.NetworkStatsService = { // Check length to determine if queue is empty and stop processing. if (this.updateQueue.length < 1) { this.isQueueRunning = false; return; } // Call the update function for the next element. - this.update(this.updateQueue[0].netId, this.processQueue.bind(this)); + this.update(this.updateQueue[0].type, this.processQueue.bind(this)); }, - update: function update(aNetId, aCallback) { + update: function update(connectionType, callback) { // Check if connection type is valid. - if (!this._networks[aNetId]) { - if (aCallback) { - aCallback(false, "Invalid network " + aNetId); + if (!this._connectionTypes[connectionType]) { + if (callback) { + callback(false, "Invalid network type " + connectionType); } return; } - let interfaceName = this._networks[aNetId].interfaceName; - debug("Update stats for " + interfaceName); + if (DEBUG) { + debug("Update stats for " + this._connectionTypes[connectionType].name); + } // Request stats to NetworkManager, which will get stats from netd, passing // 'networkStatsAvailable' as a callback. - if (interfaceName) { - networkManager.getNetworkInterfaceStats(interfaceName, - this.networkStatsAvailable.bind(this, aCallback, aNetId)); + let networkName = this._connectionTypes[connectionType].network.name; + if (networkName) { + networkManager.getNetworkInterfaceStats(networkName, + this.networkStatsAvailable.bind(this, callback, connectionType)); return; } - - if (aCallback) { - aCallback(true, "ok"); + if (callback) { + callback(true, "ok"); } }, /* * Callback of request stats. Store stats in database. */ - networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId, - aResult, aRxBytes, - aTxBytes, aDate) { - if (!aResult) { - if (aCallback) { - aCallback(false, "Netd IPC error"); + networkStatsAvailable: function networkStatsAvailable(callback, connType, result, rxBytes, txBytes, date) { + if (!result) { + if (callback) { + callback(false, "Netd IPC error"); } return; } - let stats = { appId: 0, - networkId: this._networks[aNetId].network.id, - networkType: this._networks[aNetId].network.type, - date: aDate, - rxBytes: aTxBytes, - txBytes: aRxBytes }; + let stats = { appId: 0, + connectionType: this._connectionTypes[connType].name, + date: date, + rxBytes: rxBytes, + txBytes: txBytes }; - debug("Update stats for: " + JSON.stringify(stats)); - - this._db.saveStats(stats, function onSavedStats(aError, aResult) { - if (aCallback) { - if (aError) { - aCallback(false, aError); + if (DEBUG) { + debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes + + " tx=" + stats.txBytes + " timestamp=" + stats.date); + } + this._db.saveStats(stats, function onSavedStats(error, result) { + if (callback) { + if (error) { + callback(false, error); return; } - aCallback(true, "OK"); + callback(true, "OK"); } }); }, /* * Function responsible for receiving per-app stats. */ - saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, aRxBytes, aTxBytes, aCallback) { - let netId = this.convertNetworkInterface(aNetwork); - if (!netId) { - if (aCallback) { - aCallback.notify(false, "Invalid network type"); - } - return; + saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) { + if (DEBUG) { + debug("saveAppStats: " + aAppId + " " + aConnectionType + " " + + aTimeStamp + " " + aRxBytes + " " + aTxBytes); } - debug("saveAppStats: " + aAppId + " " + netId + " " + - aTimeStamp + " " + aRxBytes + " " + aTxBytes); - // Check if |aAppId| and |aConnectionType| are valid. - if (!aAppId || !this._networks[netId]) { - debug("Invalid appId or network interface"); + if (!aAppId || aConnectionType == NET_TYPE_UNKNOWN) { return; } let stats = { appId: aAppId, - networkId: this._networks[netId].network.id, - networkType: this._networks[netId].network.type, + 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 + "" + netId; + 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) { @@ -546,25 +473,29 @@ this.NetworkStatsService = { // 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) { - debug("Application stats inserted in indexedDB"); + if (DEBUG) { + debug("Application stats inserted in indexedDB"); + } } ); delete this.cachedAppStats[key]; } }, - updateCachedAppStats: function updateCachedAppStats(aCallback) { - debug("updateCachedAppStats: " + this.cachedAppStatsDate); + 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; @@ -573,47 +504,47 @@ this.NetworkStatsService = { 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 (!aCallback) { + if (!callback) { return; } if (error) { - aCallback(false, error); + callback(false, error); return; } - aCallback(true, "ok"); + 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(aError, aResult) { - if (aError) { - debug("Error: " + aError); + this._db.logAllRecords(function onResult(error, result) { + if (error) { + debug("Error: " + error); return; } debug("===== LOG ====="); - debug("There are " + aResult.length + " items"); - debug(JSON.stringify(aResult)); + debug("There are " + result.length + " items"); + debug(JSON.stringify(result)); }); - }, + } }; NetworkStatsService.init();
--- a/dom/network/src/NetworkStatsServiceProxy.js +++ b/dom/network/src/NetworkStatsServiceProxy.js @@ -24,24 +24,24 @@ function NetworkStatsServiceProxy() { } } NetworkStatsServiceProxy.prototype = { /* * Function called in the protocol layer (HTTP, FTP, WebSocket ...etc) * to pass the per-app stats to NetworkStatsService. */ - saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, + saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) { if (DEBUG) { - debug("saveAppStats: " + aAppId + " connectionType " + aNetwork.type + - " " + aTimeStamp + " " + aRxBytes + " " + aTxBytes); + debug("saveAppStats: " + aAppId + " " + aConnectionType + " " + + aTimeStamp + " " + aRxBytes + " " + aTxBytes); } - NetworkStatsService.saveAppStats(aAppId, aNetwork, aTimeStamp, - aRxBytes, aTxBytes, aCallback); + NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp, + aRxBytes, aTxBytes, aCallback); }, classID : NETWORKSTATSSERVICEPROXY_CID, QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]), } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);
--- a/dom/network/src/TCPSocket.js +++ b/dom/network/src/TCPSocket.js @@ -162,17 +162,17 @@ TCPSocket.prototype = { _waitingForStartTLS: false, _pendingDataAfterStartTLS: [], #ifdef MOZ_WIDGET_GONK // Network statistics (Gonk-specific feature) _txBytes: 0, _rxBytes: 0, _appId: Ci.nsIScriptSecurityManager.NO_APP_ID, - _activeNetwork: null, + _connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN, #endif // Public accessors. get readyState() { return this._readyState; }, get binaryType() { return this._binaryType; @@ -342,17 +342,17 @@ TCPSocket.prototype = { } let nssProxy = Cc["@mozilla.org/networkstatsServiceProxy;1"] .getService(Ci.nsINetworkStatsServiceProxy); if (!nssProxy) { LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available."); return; } - nssProxy.saveAppStats(this._appId, this._activeNetwork, Date.now(), + nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(), this._rxBytes, this._txBytes); // Reset the counters once the statistics is saved to NetworkStatsServiceProxy. this._txBytes = this._rxBytes = 0; }, // End of helper method for network statistics. #endif @@ -525,22 +525,22 @@ TCPSocket.prototype = { return that; } let transport = that._transport = this._createTransport(host, port, that._ssl); transport.setEventSink(that, Services.tm.currentThread); that._initStream(that._binaryType); #ifdef MOZ_WIDGET_GONK - // Set _activeNetwork, which is only required for network statistics. + // Set _connectionType, which is only required for network statistics. // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is // Gonk-specific. let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager); - if (networkManager) { - that._activeNetwork = networkManager.active; + if (networkManager && networkManager.active) { + that._connectionType = networkManager.active.type; } #endif return that; }, upgradeToSecure: function ts_upgradeToSecure() { if (this._readyState !== kOPEN) {
--- a/dom/network/tests/test_networkstats_basics.html +++ b/dom/network/tests/test_networkstats_basics.html @@ -7,44 +7,55 @@ </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> +// Test for NetworkStats +function checkInterface(aInterface) { + ok(!(aInterface in window), aInterface + " should be prefixed"); + ok(("Moz" + aInterface) in window, aInterface + " should be prefixed"); +} + function test() { + // Test interfaces + checkInterface("NetworkStatsManager"); + checkInterface("NetworkStats"); + checkInterface("NetworkStatsData"); + ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist"); ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object"); netStats = navigator.mozNetworkStats; // Test IDL attributes - ok('availableNetworks' in netStats, - "availableNetworks should be a NetworkStats attribute"); - ok(Array.isArray(netStats.availableNetworks) && netStats.availableNetworks.length > 0, - "availableNetworks is an array not empty."); + ok('connectionTypes' in netStats, + "connectionTypes should be a NetworkStats attribute"); + ok(Array.isArray(netStats.connectionTypes) && netStats.connectionTypes.length > 0, + "connectionTypes is an array not empty."); ok('sampleRate' in netStats, "sampleRate should be a NetworkStats attribute"); ok(netStats.sampleRate > 0, "sampleRate is greater than 0."); - ok('maxStorageAge' in netStats, - "maxStorageAge should be a NetworkStats attribute"); - ok(netStats.maxStorageAge > 0, - "maxStorageAge is greater than 0."); + ok('maxStorageSamples' in netStats, + "maxStorageSamples should be a NetworkStats attribute"); + ok(netStats.maxStorageSamples > 0, + "maxStorageSamples is greater than 0."); // Test IDL methods next(); return; } -function checkDataDates(data, start, end, sampleRate) { +function checkDataDates(data, start, end, sampleRate){ var offset = (new Date()).getTimezoneOffset() * 60 * 1000; start = Math.floor((start.getTime() - offset) / sampleRate) * sampleRate + offset; end = Math.floor((end.getTime() - offset) / sampleRate) * sampleRate + offset; var counter = 0; var date = start; var success = true; @@ -55,195 +66,292 @@ function checkDataDates(data, start, end } date += sampleRate; counter++; } while (date <= end); ok(success, "data result has correct dates"); } -function compareNetworks(networkA, networkB) { - return (networkA.id == networkB.id && - networkA.type == networkB.type); -} - var req; var index = -1; var netStats = null; var steps = [ function () { - // Test clearAllStats - req = netStats.clearAllStats(); + // Test clearAlldata + req = netStats.clearAllData(); req.onsuccess = function () { - ok(true, "clearAllStats deleted the database"); + ok(true, "clearAllData deleted the database"); next(); }; req.onerror = function () { - ok(false, "clearAllStats deleted the database"); + ok(false, "clearAllData deleted the database"); } }, function () { - // Check if getSamples launch exception when start is greather than end + // Check if getNetworkStats launch exception when start is greather than end // Prepare get params - var network = netStats.availableNetworks[0]; + var type = netStats.connectionTypes[0]; // Get dates var endDate = new Date(); var startDate = new Date(endDate.getTime() + 1000); try { - netStats.getSamples(network, startDate, endDate); + netStats.getNetworkStats({start: startDate, end: endDate}); } catch(ex) { - ok(true, "getSamples launch exception when start is greater than end"); - next(); - return; - } - - ok(false, "getSamples launch exception when start is greater than end"); - next(); - return; - }, - function () { - // Test if call getSamples with network of type different than - // nsIDOMMozNetworkStatsInterface launch an exception - - // Prepare get params - var network = "wifi"; - var endDate = new Date(); - var startDate = new Date(endDate.getTime() - 1000); - - try { - netStats.getSamples(network, new Date(), new Date()); - } catch(ex) { - ok(true, "getSamples launch exception if network is not " + - "a nsIDOMMozNetworkStatsInterface"); + ok(true, "getNetworkStats launch exception when start is greater than end"); next(); return; } - ok(false, "getSamples launch exception if network is not " + - "a nsIDOMMozNetworkStatsInterface"); + ok(false, "getNetworkStats launch exceptionwhen start is greater than end"); + next(); + return; }, function () { - // Test if call getSamples with start parameter type different than Date launch an exception + // Test if call getNetworkStats with undefined start param launch an exception // Prepare get params - var network = netStats.availableNetworks[0]; - var endDate = new Date(); - var startDate = new Date(endDate.getTime() - 1000); - startDate = startDate.toString(); + var type = netStats.connectionTypes[0]; + setTimeout(function() { + try { + netStats.getNetworkStats({end: new Date()}); + } catch(ex) { + ok(true, "getNetworkStats launch exception when start param does not exist"); + next(); + return; + } - try { - netStats.getSamples(network, startDate, endDate); - } catch(ex) { - ok(true, "getSamples launch exception when start param is not a Date"); - next(); - return; - } + ok(false, "getNetworkStats launch exception when start param does not exist"); + }, 1000); + }, + function () { + // Test if call getNetworkStats with undefined end param launch an exception - ok(false, "getSamples launch exception when start param is not a Date"); + // Prepare get params + var type = netStats.connectionTypes[0]; + setTimeout(function() { + try { + netStats.getNetworkStats({start: new Date()}); + } catch(ex) { + ok(true, "getNetworkStats launch exception when end param does not exist"); + next(); + return; + } + + ok(false, "getNetworkStats launch exception when end param does not exist"); + }, 1000); }, function () { - // Test if call getSamples with end parameter type different than Date launch an exception - + ok(true, "Get system stats for a connectionType and dates adapted to samplerate"); // Prepare get params - var network = netStats.availableNetworks[0]; - var endDate = new Date(); - var startDate = new Date(endDate.getTime() - 1000); - endDate = startDate.toString(); + 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; - try { - netStats.getSamples(network, startDate, endDate); - } catch(ex) { - ok(true, "getSamples launch exception when end param is not a Date"); + // Launch request + req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type}); + req.onsuccess = function () { + 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"); + 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(); - return; + }; + req.onerror = function () { + ok(false, "Get system stats for a connectionType failure!"); } - - ok(false, "getSamples launch exception when end param is not a Date"); }, function () { - ok(true, "Get stats for a network and dates adapted to samplerate"); + ok(true, "Get system stats for all connectionTypes and dates adapted to samplerate"); // Prepare get params - var network = netStats.availableNetworks[0]; var diff = 2; // Get samplerate in millis - var sampleRate = netStats.sampleRate; + 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.getSamples(network, startDate, endDate); + req = netStats.getNetworkStats({start: startDate, end: endDate}); req.onsuccess = function () { - ok(true, "Get system stats request ok"); + ok(true, "Get stats request ok"); ok(req.result.manifestURL == null, "manifestURL should be null"); - ok(compareNetworks(req.result.network, network), "networks 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 system stats for all connectionTypes failure!"); + } + }, + function () { + 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 stats failure!"); + ok(false, "Get app stats for a connectionType failure!"); } }, function () { - ok(true, "Get system stats for a network and dates not adapted to samplerate"); + ok(true, "Get app stats for all connectionTypes and dates adapted to samplerate"); // Prepare get params - var network = netStats.availableNetworks[0]; + var url = 'app://browser.gaiamobile.org/manifest.webapp'; var diff = 2; // Get samplerate in millis - var sampleRate = netStats.sampleRate; + 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; + // Get samplerate in millis + var sampleRate = netStats.sampleRate * 1000; var endDate = new Date(); var startDate = new Date(endDate.getTime() - (sampleRate * diff)); // Calculate the number of samples that should be returned based on the // the samplerate, including final and initial samples and taking into // account that these will be filtered according to precision. var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate - Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1; // Launch request - req = netStats.getSamples(network, startDate, endDate); + 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(compareNetworks(req.result.network, network), "networks 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 stats failure!"); + ok(false, "Get system stats for a connectionType failure!"); } }, function () { - // Test clearStats - var network = netStats.availableNetworks[0]; + ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate"); + // Prepare get params + var diff = 2; + // Get samplerate in millis + var sampleRate = netStats.sampleRate * 1000; + // Get date with samplerate's precision + var endDate = new Date(); + var startDate = new Date(endDate.getTime() - (sampleRate * diff)); + // Calculate the number of samples that should be returned based on the + // the samplerate, including final and initial samples and taking into + // account that these will be filtered according to precision. + var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate - + Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1; - req = netStats.clearStats(network); + // Launch request + req = netStats.getNetworkStats({start: startDate, end: endDate}); req.onsuccess = function () { - ok(true, "clearStats deleted the database"); + 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"); + 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, "clearStats deleted the database"); + ok(false, "Get system stats for all connectionType failure!"); } }, function () { ok(true, "all done!\n"); SpecialPowers.removePermission("networkstats-manage", document); SimpleTest.finish(); return; }
--- a/dom/network/tests/test_networkstats_enabled_no_perm.html +++ b/dom/network/tests/test_networkstats_enabled_no_perm.html @@ -7,29 +7,29 @@ </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> -// Test to ensure NetworkStats is enabled but mozNetworkStats.availableNetworks +// Test to ensure NetworkStats is enabled but mozNetworkStats.connectionTypes // does not work in content. SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true); SpecialPowers.removePermission("networkstats-manage", document); ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should be accessible if dom.mozNetworkStats.enabled is true"); var error; try { - navigator.mozNetworkStats.availableNetworks; - ok(false, "Accessing navigator.mozNetworkStats.availableNetworks should have thrown!"); + navigator.mozNetworkStats.connectionTypes; + ok(false, "Accessing navigator.mozNetworkStats.connectionTypes should have thrown!"); } catch (ex) { error = ex; } -ok(error, "Got an exception accessing navigator.mozNetworkStats.availableNetworks"); +ok(error, "Got an exception accessing navigator.mozNetworkStats.connectionTypes"); </script> </pre> </body> </html>
--- a/dom/network/tests/unit_stats/test_networkstats_db.js +++ b/dom/network/tests/unit_stats/test_networkstats_db.js @@ -1,32 +1,23 @@ /* 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/NetworkStatsDB.jsm"); -const netStatsDb = new NetworkStatsDB(); +const netStatsDb = new NetworkStatsDB(this); function filterTimestamp(date) { var sampleRate = netStatsDb.sampleRate; var offset = date.getTimezoneOffset() * 60 * 1000; return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate; } -function getNetworks() { - return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI }, - { id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }]; -} - -function compareNetworks(networkA, networkB) { - return (networkA[0] == networkB[0] && networkA[1] == networkB[1]); -} - add_test(function test_sampleRate() { var sampleRate = netStatsDb.sampleRate; do_check_true(sampleRate > 0); netStatsDb.sampleRate = 0; sampleRate = netStatsDb.sampleRate; do_check_true(sampleRate > 0); run_next_test(); @@ -93,99 +84,80 @@ add_test(function test_fillResultSamples aux += sampleRate; } do_check_true(success); run_next_test(); }); add_test(function test_clear() { - var networks = getNetworks(); - netStatsDb.clearStats(networks, function (error, result) { - do_check_eq(error, null); - run_next_test(); - }); -}); - -add_test(function test_clear_interface() { - var networks = getNetworks(); - netStatsDb.clearInterfaceStats(networks[0], function (error, result) { + netStatsDb.clear(function (error, result) { do_check_eq(error, null); run_next_test(); }); }); add_test(function test_internalSaveStats_singleSample() { - var networks = getNetworks(); - - var stats = { appId: 0, - network: [networks[0].id, networks[0].type], - timestamp: Date.now(), - rxBytes: 0, - txBytes: 0, - rxTotalBytes: 1234, - txTotalBytes: 1234 }; + var stats = {appId: 0, + connectionType: "wifi", + timestamp: Date.now(), + rxBytes: 0, + txBytes: 0, + rxTotalBytes: 1234, + txTotalBytes: 1234}; netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb._saveStats(txn, store, 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_true(compareNetworks(result[0].network, stats.network)); + do_check_eq(result[0].connectionType, stats.connectionType); do_check_eq(result[0].timestamp, stats.timestamp); do_check_eq(result[0].rxBytes, stats.rxBytes); do_check_eq(result[0].txBytes, stats.txBytes); do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes); do_check_eq(result[0].txTotalBytes, stats.txTotalBytes); run_next_test(); }); }); }); add_test(function test_internalSaveStats_arraySamples() { - var networks = getNetworks(); - - netStatsDb.clearStats(networks, function (error, result) { + netStatsDb.clear(function (error, result) { do_check_eq(error, null); - var network = [networks[0].id, networks[0].type]; - var samples = 2; var stats = []; for (var i = 0; i < samples; i++) { - stats.push({ appId: 0, - network: network, - timestamp: Date.now() + (10 * i), - rxBytes: 0, - txBytes: 0, - rxTotalBytes: 1234, - txTotalBytes: 1234 }); + stats.push({appId: 0, + connectionType: "wifi", + timestamp: Date.now() + (10 * i), + rxBytes: 0, + txBytes: 0, + rxTotalBytes: 1234, + txTotalBytes: 1234}); } netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb._saveStats(txn, store, stats); }, function(error, result) { do_check_eq(error, null); netStatsDb.logAllRecords(function(error, result) { do_check_eq(error, null); - - // Result has one sample more than samples because clear inserts - // an empty sample to keep totalBytes synchronized with netd counters - result.shift(); do_check_eq(result.length, samples); var success = true; - for (var i = 1; i < samples; i++) { + for (var i = 0; i < samples; i++) { if (result[i].appId != stats[i].appId || - !compareNetworks(result[i].network, stats[i].network) || + result[i].connectionType != stats[i].connectionType || result[i].timestamp != stats[i].timestamp || result[i].rxBytes != stats[i].rxBytes || result[i].txBytes != stats[i].txBytes || result[i].rxTotalBytes != stats[i].rxTotalBytes || result[i].txTotalBytes != stats[i].txTotalBytes) { success = false; break; } @@ -193,62 +165,59 @@ add_test(function test_internalSaveStats do_check_true(success); run_next_test(); }); }); }); }); add_test(function test_internalRemoveOldStats() { - var networks = getNetworks(); - - netStatsDb.clearStats(networks, function (error, result) { + netStatsDb.clear(function (error, result) { do_check_eq(error, null); - var network = [networks[0].id, networks[0].type]; var samples = 10; var stats = []; for (var i = 0; i < samples - 1; i++) { - stats.push({ appId: 0, - network: network, timestamp: Date.now() + (10 * i), - rxBytes: 0, txBytes: 0, - rxTotalBytes: 1234, txTotalBytes: 1234 }); + stats.push({appId: 0, + connectionType: "wifi", timestamp: Date.now() + (10 * i), + rxBytes: 0, txBytes: 0, + rxTotalBytes: 1234, txTotalBytes: 1234}); } - stats.push({ appId: 0, - network: network, 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 + var date = stats[stats.length -1].timestamp + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1; - netStatsDb._removeOldStats(txn, store, 0, network, date); + netStatsDb._removeOldStats(txn, store, 0, "wifi", date); }, function(error, result) { do_check_eq(error, null); netStatsDb.logAllRecords(function(error, result) { do_check_eq(error, null); do_check_eq(result.length, 1); run_next_test(); }); }); }); }); -function processSamplesDiff(networks, lastStat, newStat, callback) { - netStatsDb.clearStats(networks, function (error, result){ +function processSamplesDiff(lastStat, newStat, callback) { + netStatsDb.clear(function (error, result){ do_check_eq(error, null); netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb._saveStats(txn, store, lastStat); }, function(error, result) { netStatsDb.dbNewTxn("readwrite", function(txn, store) { - let request = store.index("network").openCursor(newStat.network, "prev"); + let request = store.index("connectionType").openCursor(newStat.connectionType, "prev"); request.onsuccess = function onsuccess(event) { let cursor = event.target.result; do_check_neq(cursor, null); netStatsDb._processSamplesDiff(txn, store, cursor, newStat); }; }, function(error, result) { do_check_eq(error, null); netStatsDb.logAllRecords(function(error, result) { @@ -256,314 +225,292 @@ function processSamplesDiff(networks, la callback(result); }); }); }); }); } add_test(function test_processSamplesDiffSameSample() { - var networks = getNetworks(); - var network = [networks[0].id, networks[0].type]; - var sampleRate = netStatsDb.sampleRate; var date = filterTimestamp(new Date()); - - var lastStat = { appId: 0, - network: network, timestamp: date, - rxBytes: 0, txBytes: 0, - rxTotalBytes: 1234, txTotalBytes: 1234 }; + var lastStat = {appId: 0, + connectionType: "wifi", timestamp: date, + rxBytes: 0, txBytes: 0, + rxTotalBytes: 1234, txTotalBytes: 1234}; - var newStat = { appId: 0, - network: network, timestamp: date, - rxBytes: 0, txBytes: 0, - rxTotalBytes: 2234, txTotalBytes: 2234 }; + var newStat = {appId: 0, + connectionType: "wifi", timestamp: date, + rxBytes: 0, txBytes: 0, + rxTotalBytes: 2234, txTotalBytes: 2234}; - processSamplesDiff(networks, lastStat, newStat, function(result) { + processSamplesDiff(lastStat, newStat, function(result) { do_check_eq(result.length, 1); do_check_eq(result[0].appId, newStat.appId); - do_check_true(compareNetworks(result[0].network, newStat.network)); + 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); do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes); do_check_eq(result[0].rxTotalBytes, newStat.rxTotalBytes); do_check_eq(result[0].txTotalBytes, newStat.txTotalBytes); run_next_test(); }); }); add_test(function test_processSamplesDiffNextSample() { - var networks = getNetworks(); - var network = [networks[0].id, networks[0].type]; - var sampleRate = netStatsDb.sampleRate; var date = filterTimestamp(new Date()); - - var lastStat = { appId: 0, - network: network, timestamp: date, - rxBytes: 0, txBytes: 0, - rxTotalBytes: 1234, txTotalBytes: 1234 }; + var lastStat = {appId: 0, + connectionType: "wifi", timestamp: date, + rxBytes: 0, txBytes: 0, + rxTotalBytes: 1234, txTotalBytes: 1234}; - var newStat = { appId: 0, - network: network, timestamp: date + sampleRate, - rxBytes: 0, txBytes: 0, - rxTotalBytes: 500, txTotalBytes: 500 }; + var newStat = {appId: 0, + connectionType: "wifi", timestamp: date + sampleRate, + rxBytes: 0, txBytes: 0, + rxTotalBytes: 500, txTotalBytes: 500}; - processSamplesDiff(networks, lastStat, newStat, function(result) { + processSamplesDiff(lastStat, newStat, function(result) { do_check_eq(result.length, 2); do_check_eq(result[1].appId, newStat.appId); - do_check_true(compareNetworks(result[1].network, newStat.network)); + do_check_eq(result[1].connectionType, newStat.connectionType); do_check_eq(result[1].timestamp, newStat.timestamp); do_check_eq(result[1].rxBytes, newStat.rxTotalBytes); do_check_eq(result[1].txBytes, newStat.txTotalBytes); do_check_eq(result[1].rxTotalBytes, newStat.rxTotalBytes); do_check_eq(result[1].txTotalBytes, newStat.txTotalBytes); run_next_test(); }); }); add_test(function test_processSamplesDiffSamplesLost() { - var networks = getNetworks(); - var network = [networks[0].id, networks[0].type]; var samples = 5; var sampleRate = netStatsDb.sampleRate; var date = filterTimestamp(new Date()); - var lastStat = { appId: 0, - network: network, timestamp: date, - rxBytes: 0, txBytes: 0, - rxTotalBytes: 1234, txTotalBytes: 1234 }; + var lastStat = {appId: 0, + connectionType: "wifi", timestamp: date, + rxBytes: 0, txBytes: 0, + rxTotalBytes: 1234, txTotalBytes: 1234}; - var newStat = { appId: 0, - network: network, timestamp: date + (sampleRate * samples), - rxBytes: 0, txBytes: 0, - rxTotalBytes: 2234, txTotalBytes: 2234 }; + var newStat = {appId: 0, + connectionType: "wifi", timestamp: date + (sampleRate * samples), + rxBytes: 0, txBytes: 0, + rxTotalBytes: 2234, txTotalBytes: 2234}; - processSamplesDiff(networks, lastStat, newStat, function(result) { + processSamplesDiff(lastStat, newStat, function(result) { do_check_eq(result.length, samples + 1); do_check_eq(result[0].appId, newStat.appId); - do_check_true(compareNetworks(result[samples].network, newStat.network)); + 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); do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes); do_check_eq(result[samples].rxTotalBytes, newStat.rxTotalBytes); do_check_eq(result[samples].txTotalBytes, newStat.txTotalBytes); run_next_test(); }); }); add_test(function test_saveStats() { - var networks = getNetworks(); - var network = [networks[0].id, networks[0].type]; + var stats = {appId: 0, + connectionType: "wifi", + date: new Date(), + rxBytes: 2234, + txBytes: 2234}; - var stats = { appId: 0, - networkId: networks[0].id, - networkType: networks[0].type, - date: new Date(), - rxBytes: 2234, - txBytes: 2234}; - - netStatsDb.clearStats(networks, function (error, result) { + 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_true(compareNetworks(result[0].network, network)); + 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, 0); do_check_eq(result[0].txBytes, 0); do_check_eq(result[0].rxTotalBytes, stats.rxBytes); do_check_eq(result[0].txTotalBytes, stats.txBytes); run_next_test(); }); }); }); }); add_test(function test_saveAppStats() { - var networks = getNetworks(); - var network = [networks[0].id, networks[0].type]; + var stats = {appId: 1, + connectionType: "wifi", + date: new Date(), + rxBytes: 2234, + txBytes: 2234}; - var stats = { appId: 1, - networkId: networks[0].id, - networkType: networks[0].type, - date: new Date(), - rxBytes: 2234, - txBytes: 2234}; - - netStatsDb.clearStats(networks, function (error, result) { + 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); - // The clear function clears all records of the datbase but - // inserts a new element for each [appId, connectionId, connectionType] - // record to keep the track of rxTotalBytes / txTotalBytes. - // So at this point, we have two records, one for the appId 0 used in - // past tests and the new one for appId 1 - do_check_eq(result.length, 2); - do_check_eq(result[1].appId, stats.appId); - do_check_true(compareNetworks(result[1].network, network)); + 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[1].timestamp, timestamp); - do_check_eq(result[1].rxBytes, stats.rxBytes); - do_check_eq(result[1].txBytes, stats.txBytes); - do_check_eq(result[1].rxTotalBytes, 0); - do_check_eq(result[1].txTotalBytes, 0); + 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(network, stats, callback) { - netStatsDb.clearStats(network, function (error, result) { +function prepareFind(stats, callback) { + netStatsDb.clear(function (error, result) { do_check_eq(error, null); netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb._saveStats(txn, store, stats); }, function(error, result) { callback(error, result); }); }); } add_test(function test_find () { - var networks = getNetworks(); - var networkWifi = [networks[0].id, networks[0].type]; - var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface - var appId = 0; - 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: appId, - network: networkWifi, timestamp: saveDate + (sampleRate * i), - rxBytes: 0, txBytes: 10, - rxTotalBytes: 0, txTotalBytes: 0 }); + stats.push({appId: 0, + connectionType: "wifi", timestamp: saveDate + (sampleRate * i), + rxBytes: 0, txBytes: 10, + rxTotalBytes: 0, txTotalBytes: 0}); - stats.push({ appId: appId, - network: networkMobile, 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(networks[0], stats, function(error, result) { + prepareFind(stats, function(error, result) { do_check_eq(error, null); netStatsDb.find(function (error, result) { do_check_eq(error, null); - do_check_eq(result.network.id, networks[0].id); - do_check_eq(result.network.type, networks[0].type); + 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); - run_next_test(); - }, networks[0], start, end, appId); + + 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(); + }, {appId: 0, start: start, end: end}); + }, {start: start, end: end, connectionType: "wifi", appId: 0}); }); }); add_test(function test_findAppStats () { - var networks = getNetworks(); - var networkWifi = [networks[0].id, networks[0].type]; - var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface - 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, - network: networkWifi, timestamp: saveDate + (sampleRate * i), - rxBytes: 0, txBytes: 10, - rxTotalBytes: 0, txTotalBytes: 0 }); + stats.push({appId: 1, + connectionType: "wifi", timestamp: saveDate + (sampleRate * i), + rxBytes: 0, txBytes: 10, + rxTotalBytes: 0, txTotalBytes: 0}); - stats.push({ appId: 1, - network: networkMobile, 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(networks[0], stats, function(error, result) { + prepareFind(stats, function(error, result) { do_check_eq(error, null); netStatsDb.find(function (error, result) { do_check_eq(error, null); - do_check_eq(result.network.id, networks[0].id); - do_check_eq(result.network.type, networks[0].type); + 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); - run_next_test(); - }, networks[0], start, end, 1); + + 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 networks = getNetworks(); - var networkWifi = networks[0]; - var networkMobile = networks[1]; // Fake mobile interface - var saveDate = filterTimestamp(new Date()); var cached = Object.create(null); - cached['1wifi'] = { appId: 1, date: new Date(), - networkId: networkWifi.id, networkType: networkWifi.type, - rxBytes: 0, txBytes: 10 }; + cached['1wifi'] = {appId: 1, + connectionType: "wifi", date: new Date(), + rxBytes: 0, txBytes: 10}; - cached['1mobile'] = { appId: 1, date: new Date(), - networkId: networkMobile.id, networkType: networkMobile.type, - rxBytes: 0, txBytes: 10 }; + cached['1mobile'] = {appId: 1, + connectionType: "mobile", date: new Date(), + rxBytes: 0, txBytes: 10}; - cached['2wifi'] = { appId: 2, date: new Date(), - networkId: networkWifi.id, networkType: networkWifi.type, - rxBytes: 0, txBytes: 10 }; + cached['2wifi'] = {appId: 2, + connectionType: "wifi", date: new Date(), + rxBytes: 0, txBytes: 10}; - cached['2mobile'] = { appId: 2, date: new Date(), - networkId: networkMobile.id, networkType: networkMobile.type, - 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; - networks.push(networkMobile); - netStatsDb.clearStats(networks, function (error, result) { + 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) { - // Again, result has two samples more than expected samples because - // clear inserts one empty sample for each network to keep totalBytes - // synchronized with netd counters. so the first two samples have to - // be discarted. - result.shift(); - result.shift(); - do_check_eq(error, null); do_check_eq(result.length, 4); do_check_eq(result[0].appId, 1); - do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type])); + 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);
--- a/dom/network/tests/unit_stats/test_networkstats_service.js +++ b/dom/network/tests/unit_stats/test_networkstats_service.js @@ -1,64 +1,56 @@ /* 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; add_test(function test_clearDB() { - var networks = NetworkStatsService.availableNetworks(); - NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) { + NetworkStatsService._db.clear(function onDBCleared(error, result) { do_check_eq(result, null); run_next_test(); }); }); -function getNetworkId() { - var network = (NetworkStatsService.availableNetworks())[0]; - return NetworkStatsService.getNetworkId(network.id, network.type); -} add_test(function test_networkStatsAvailable_ok() { - var netId = getNetworkId(); NetworkStatsService.networkStatsAvailable(function (success, msg) { do_check_eq(success, true); run_next_test(); - }, netId, true, 1234, 4321, new Date()); + }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, true, 1234, 4321, new Date()); }); add_test(function test_networkStatsAvailable_failure() { - var netId = getNetworkId(); NetworkStatsService.networkStatsAvailable(function (success, msg) { do_check_eq(success, false); run_next_test(); - }, netId, false, 1234, 4321, new Date()); + }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, false, 1234, 4321, new Date()); }); -add_test(function test_update_invalidNetwork() { +add_test(function test_update_invalidConnection() { NetworkStatsService.update(-1, function (success, msg) { do_check_eq(success, false); - do_check_eq(msg, "Invalid network -1"); + do_check_eq(msg, "Invalid network type -1"); run_next_test(); }); }); add_test(function test_update() { - var netId = getNetworkId(); - NetworkStatsService.update(netId, function (success, msg) { + NetworkStatsService.update(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function (success, msg) { do_check_eq(success, true); run_next_test(); }); }); add_test(function test_updateQueueIndex() { - NetworkStatsService.updateQueue = [{netId: 0, callbacks: null}, - {netId: 1, callbacks: null}, - {netId: 2, callbacks: null}, - {netId: 3, callbacks: null}, - {netId: 4, callbacks: null}]; + NetworkStatsService.updateQueue = [{type: 0, callbacks: null}, + {type: 1, callbacks: null}, + {type: 2, callbacks: null}, + {type: 3, callbacks: null}, + {type: 4, callbacks: null}]; var index = NetworkStatsService.updateQueueIndex(3); do_check_eq(index, 3); index = NetworkStatsService.updateQueueIndex(10); do_check_eq(index, -1); NetworkStatsService.updateQueue = []; run_next_test(); }); @@ -66,54 +58,48 @@ add_test(function test_updateQueueIndex( add_test(function test_updateAllStats() { NetworkStatsService.updateAllStats(function(success, msg) { do_check_eq(success, true); run_next_test(); }); }); add_test(function test_updateStats_ok() { - var netId = getNetworkId(); - NetworkStatsService.updateStats(netId, function(success, msg){ + NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function(success, msg){ do_check_eq(success, true); run_next_test(); }); }); add_test(function test_updateStats_failure() { NetworkStatsService.updateStats(-1, function(success, msg){ do_check_eq(success, false); run_next_test(); }); }); add_test(function test_queue() { - // Fill networks with fake network interfaces + // Fill connections with fake network interfaces (wlan0 and rmnet0) // to enable netd async requests - var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; - var netId1 = NetworkStatsService.getNetworkId(network.id, network.type); - NetworkStatsService._networks[netId1] = { network: network, - interfaceName: "net1" }; + NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_WIFI] + .network.name = 'wlan0'; + NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE] + .network.name = 'rmnet0'; - network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; - var netId2 = NetworkStatsService.getNetworkId(network.id, network.type); - NetworkStatsService._networks[netId2] = { network: network, - interfaceName: "net2" }; - - NetworkStatsService.updateStats(netId1); - NetworkStatsService.updateStats(netId2); + NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI); + NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE); do_check_eq(NetworkStatsService.updateQueue.length, 2); do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1); var callback = function(success, msg) { return; }; - NetworkStatsService.updateStats(netId1, callback); - NetworkStatsService.updateStats(netId2, callback); + NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, callback); + NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, callback); do_check_eq(NetworkStatsService.updateQueue.length, 2); do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2); do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null); do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null); run_next_test(); });
--- a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js +++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js @@ -4,154 +4,116 @@ 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"); -function mokConvertNetworkInterface() { - NetworkStatsService.convertNetworkInterface = function(aNetwork) { - if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE && - aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { - return null; - } - - let id = '0'; - if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) { - id = '1234' - } - - let netId = this.getNetworkId(id, aNetwork.type); - - if (!this._networks[netId]) { - this._networks[netId] = Object.create(null); - this._networks[netId].network = { id: id, - type: aNetwork.type }; - } - - return netId; - }; -} - add_test(function test_saveAppStats() { var cachedAppStats = NetworkStatsService.cachedAppStats; var timestamp = NetworkStatsService.cachedAppStatsDate.getTime(); var samples = 5; - // Create to fake nsINetworkInterfaces. As nsINetworkInterface can not - // be instantiated, these two vars will emulate it by filling the properties - // that will be used. - var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"}; - var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"}; - - // Insert fake mobile network interface in NetworkStatsService - var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type); - do_check_eq(Object.keys(cachedAppStats).length, 0); for (var i = 0; i < samples; i++) { - nssProxy.saveAppStats(1, wifi, timestamp, 10, 20); + nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, + timestamp, 10, 20); - nssProxy.saveAppStats(1, mobile, timestamp, 10, 20); + nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, + timestamp, 10, 20); } - var key1 = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type); - var key2 = 1 + mobileNetId; + 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].networkId, wifi.id); - do_check_eq(cachedAppStats[key1].networkType, wifi.type); + 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].networkId, mobile.id); - do_check_eq(cachedAppStats[key2].networkType, mobile.type); + 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 wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"}; - var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"}; - - var key = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type); + 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, wifi, today.getTime(), 10, 20); + nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, + today.getTime(), 10, 20); - nssProxy.saveAppStats(1, mobile, 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 + NetworkStatsService.getNetworkId(mobile.id, mobile.type); + var key = 2 + 'mobile'; do_check_eq(Object.keys(cachedAppStats).length, 1); do_check_eq(cachedAppStats[key].appId, 2); - do_check_eq(cachedAppStats[key].networkId, mobile.id); - do_check_eq(cachedAppStats[key].networkType, mobile.type); + 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, mobile, tomorrow.getTime(), 30, 40, saveAppStatsCb); + 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; - var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"}; 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, wifi, timestamp, 10, 20); + nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, + timestamp, 10, 20); do_check_eq(Object.keys(cachedAppStats).length, 1); - nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20); + 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"); - // Function convertNetworkInterface of NetworkStatsService causes errors when dealing - // with RIL to get the iccid, so overwrite it. - mokConvertNetworkInterface(); - run_next_test(); }
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -48,16 +48,17 @@ #include "plbase64.h" #include "prmem.h" #include "prnetdb.h" #include "prbit.h" #include "zlib.h" #include <algorithm> #ifdef MOZ_WIDGET_GONK +#include "nsINetworkManager.h" #include "nsINetworkStatsServiceProxy.h" #endif // rather than slurp up all of nsIWebSocket.idl, which lives outside necko, just // dupe one constant we need from it #define CLOSE_GOING_AWAY 1001 extern PRThread *gSocketThread; @@ -961,16 +962,17 @@ WebSocketChannel::WebSocketChannel() : mCompressor(nullptr), mDynamicOutputSize(0), mDynamicOutput(nullptr), mPrivateBrowsing(false), mConnectionLogService(nullptr), mCountRecv(0), mCountSent(0), mAppId(0), + mConnectionType(NETWORK_NO_TYPE), mIsInBrowser(false) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); LOG(("WebSocketChannel::WebSocketChannel() %p\n", this)); if (!sWebSocketAdmissions) sWebSocketAdmissions = new nsWSAdmissionManager(); @@ -1075,19 +1077,19 @@ WebSocketChannel::BeginOpen() return; } // obtain app info if (localChannel) { NS_GetAppInfo(localChannel, &mAppId, &mIsInBrowser); } - // obtain active network + // obtain active connection type if (mAppId != NECKO_NO_APP_ID) { - GetActiveNetwork(); + GetConnectionType(&mConnectionType); } rv = localChannel->AsyncOpen(this, mHttpChannel); if (NS_FAILED(rv)) { LOG(("WebSocketChannel::BeginOpen: cannot async open\n")); AbortSession(NS_ERROR_CONNECTION_REFUSED); return; } @@ -3267,43 +3269,48 @@ WebSocketChannel::OnDataAvailable(nsIReq LOG(("WebSocketChannel::OnDataAvailable: HTTP data unexpected len>=%u\n", aCount)); return NS_OK; } nsresult -WebSocketChannel::GetActiveNetwork() +WebSocketChannel::GetConnectionType(int32_t *type) { #ifdef MOZ_WIDGET_GONK MOZ_ASSERT(NS_IsMainThread()); nsresult result; nsCOMPtr<nsINetworkManager> networkManager = do_GetService("@mozilla.org/network/manager;1", &result); if (NS_FAILED(result) || !networkManager) { - mActiveNetwork = nullptr; - return NS_ERROR_UNEXPECTED; + *type = NETWORK_NO_TYPE; } - result = networkManager->GetActive(getter_AddRefs(mActiveNetwork)); + nsCOMPtr<nsINetworkInterface> networkInterface; + result = networkManager->GetActive(getter_AddRefs(networkInterface)); + + if (networkInterface) { + result = networkInterface->GetType(type); + } return NS_OK; #else return NS_ERROR_NOT_IMPLEMENTED; #endif } nsresult WebSocketChannel::SaveNetworkStats(bool enforce) { #ifdef MOZ_WIDGET_GONK - // Check if the active network and app id are valid. - if(!mActiveNetwork || mAppId == NECKO_NO_APP_ID) { + // Check if the connection type and app id are valid. + if(mConnectionType == NETWORK_NO_TYPE || + mAppId == NECKO_NO_APP_ID) { return NS_OK; } if (mCountRecv <= 0 && mCountSent <= 0) { // There is no traffic, no need to save. return NS_OK; } @@ -3317,17 +3324,17 @@ WebSocketChannel::SaveNetworkStats(bool nsresult rv; nsCOMPtr<nsINetworkStatsServiceProxy> mNetworkStatsServiceProxy = do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv); if (NS_FAILED(rv)) { return rv; } - mNetworkStatsServiceProxy->SaveAppStats(mAppId, mActiveNetwork, PR_Now() / 1000, + mNetworkStatsServiceProxy->SaveAppStats(mAppId, mConnectionType, PR_Now() / 1000, mCountRecv, mCountSent, nullptr); // Reset the counters after saving. mCountSent = 0; mCountRecv = 0; return NS_OK; #else
--- a/netwerk/protocol/websocket/WebSocketChannel.h +++ b/netwerk/protocol/websocket/WebSocketChannel.h @@ -13,20 +13,16 @@ #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsITimer.h" #include "nsIDNSListener.h" #include "nsIChannelEventSink.h" #include "nsIHttpChannelInternal.h" #include "BaseWebSocketChannel.h" -#ifdef MOZ_WIDGET_GONK -#include "nsINetworkManager.h" -#endif - #include "nsCOMPtr.h" #include "nsString.h" #include "nsDeque.h" class nsIAsyncVerifyRedirectCallback; class nsIDashboardEventNotifier; class nsIEventTarget; class nsIHttpChannel; @@ -253,27 +249,26 @@ private: nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService; uint32_t mSerial; static uint32_t sSerialSeed; // These members are used for network per-app metering (bug 855949) // Currently, they are only available on gonk. public: + const static int32_t NETWORK_NO_TYPE = -1; // default conntection type const static uint64_t NETWORK_STATS_THRESHOLD = 65536; private: uint64_t mCountRecv; uint64_t mCountSent; uint32_t mAppId; + int32_t mConnectionType; bool mIsInBrowser; -#ifdef MOZ_WIDGET_GONK - nsCOMPtr<nsINetworkInterface> mActiveNetwork; -#endif - nsresult GetActiveNetwork(); + nsresult GetConnectionType(int32_t *); nsresult SaveNetworkStats(bool); void CountRecvBytes(uint64_t recvBytes) { mCountRecv += recvBytes; SaveNetworkStats(false); } void CountSentBytes(uint64_t sentBytes) {