Backed out 4 changesets (bug 887699).
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 16 Oct 2013 17:12:54 -0400
changeset 165797 423b9c30c73d12176db4bd80d38206eaa5032767
parent 165796 66af8b3096a61f6eb95059817eb0de629d0c187b
child 165808 9ab5b1d2d2b82f34ca783ce345ca1d8d3801b283
child 171420 0ca23e59862d727b1c6bdec7339d546960fd0cb3
child 178471 4dda9a646aea5bc02383ad6c2a0322637a2c4843
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs887699
milestone27.0a1
backs out5c878c48d7320232a7ecbe79354b2a1e8ece5576
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
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 4 changesets (bug 887699). Backed out changeset 5c878c48d732 (bug 887699) Backed out changeset 57b03d7055e8 (bug 887699) Backed out changeset ea06175feb4f (bug 887699) Backed out changeset 516d0f14f7fd (bug 887699)
dom/network/interfaces/nsIDOMNetworkStats.idl
dom/network/interfaces/nsIDOMNetworkStatsManager.idl
dom/network/interfaces/nsINetworkStatsServiceProxy.idl
dom/network/src/NetworkStatsDB.jsm
dom/network/src/NetworkStatsManager.js
dom/network/src/NetworkStatsManager.manifest
dom/network/src/NetworkStatsService.jsm
dom/network/src/NetworkStatsServiceProxy.js
dom/network/src/TCPSocket.js
dom/network/tests/test_networkstats_basics.html
dom/network/tests/test_networkstats_enabled_no_perm.html
dom/network/tests/unit_stats/test_networkstats_db.js
dom/network/tests/unit_stats/test_networkstats_service.js
dom/network/tests/unit_stats/test_networkstats_service_proxy.js
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannel.h
--- 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)
   {