Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 17 Oct 2013 13:49:27 +0200
changeset 165888 84cf5a56188eb202e7d267254aa210067ae9072c
parent 165887 68149d83be3a99b09cebb690eb4377d01fa832ce (current diff)
parent 165874 855da6d8a327c277e9653911b3244c3229134363 (diff)
child 165889 f7e452886b4f339ab7a3cee08fd96ae49414758d
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)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
build/mobile/robocop/robotium-solo-4.2.jar
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "86e06b1db110e34eb66826d3b1bdee3a5d57b3a7", 
+    "revision": "563d1aa93586165246ab2ab9d40566a598f56387", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -477,17 +477,17 @@
                 fieldname : textBox.getAttribute("autocompletesearchparam"),
                 value : aData },
               { handleError : function(aError) {
                   Components.utils.reportError("Saving search to form history failed: " + aError.message);
               }});
           }
           
           // null parameter below specifies HTML response for search
-          var submission = this.currentEngine.getSubmission(aData);
+          var submission = this.currentEngine.getSubmission(aData, null, "searchbar");
           BrowserSearch.recordSearchInHealthReport(this.currentEngine.name, "searchbar");
           openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="command"><![CDATA[
@@ -526,24 +526,24 @@
       <![CDATA[
         // Speculatively connect to the current engine's search URI (and
         // suggest URI, if different) to reduce request latency
 
         const SUGGEST_TYPE = "application/x-suggestions+json";
         var engine = this.currentEngine;
         var connector =
             Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect);
-        var searchURI = engine.getSubmission("dummy").uri;
+        var searchURI = engine.getSubmission("dummy", null, "searchbar").uri;
         let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                               .getInterface(Components.interfaces.nsIWebNavigation)
                               .QueryInterface(Components.interfaces.nsILoadContext);
         connector.speculativeConnect(searchURI, callbacks);
 
         if (engine.supportsResponseType(SUGGEST_TYPE)) {
-          var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri;
+          var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE, "searchbar").uri;
           if (suggestURI.prePath != searchURI.prePath)
             connector.speculativeConnect(suggestURI, callbacks);
         }
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="searchbar-textbox"
--- a/browser/components/search/test/Makefile.in
+++ b/browser/components/search/test/Makefile.in
@@ -1,12 +1,13 @@
 # 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/.
 
 ifdef ENABLE_TESTS
 pp_mochitest_browser_files := \
   browser_google.js \
+  browser_google_behavior.js \
   $(NULL)
 pp_mochitest_browser_files_PATH := $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 pp_mochitest_browser_files_FLAGS := -DMOZ_DISTRIBUTION_ID=$(MOZ_DISTRIBUTION_ID)
 PP_TARGETS += pp_mochitest_browser_files
 endif # ENABLE_TESTS
--- a/browser/components/search/test/browser_google.js
+++ b/browser/components/search/test/browser_google.js
@@ -75,16 +75,20 @@ function test() {
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
   url = engine.getSubmission("foo", null, "contextmenu").uri.spec;
   is(url, base + "&channel=rcs", "Check context menu search URL for 'foo'");
   url = engine.getSubmission("foo", null, "keyword").uri.spec;
   is(url, base + "&channel=fflb", "Check keyword search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "searchbar").uri.spec;
+  is(url, base + "&channel=sb", "Check search bar search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "homepage").uri.spec;
+  is(url, base + "&channel=np&source=hp", "Check homepage search URL for 'foo'");
 
   // Check search suggestion URL.
   url = engine.getSubmission("foo", "application/x-suggestions+json").uri.spec;
   is(url, "https://www.google.com/complete/search?client=firefox&q=foo", "Check search suggestion URL for 'foo'");
 
   // Check all other engine properties.
   const EXPECTED_ENGINE = {
     name: "Google",
@@ -144,16 +148,21 @@ function test() {
             },
             {
               "name": "channel",
               "value": "fflb",
               "purpose": "keyword",
             },
             {
               "name": "channel",
+              "value": "sb",
+              "purpose": "searchbar",
+            },
+            {
+              "name": "channel",
               "value": "np",
               "purpose": "homepage",
             },
             {
               "name": "source",
               "value": "hp",
               "purpose": "homepage",
             },
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_google_behavior.js
@@ -0,0 +1,187 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Test Google search plugin URLs
+ */
+
+"use strict";
+
+const BROWSER_SEARCH_PREF      = "browser.search.";
+
+const MOZ_PARAM_LOCALE         = /\{moz:locale\}/g;
+const MOZ_PARAM_DIST_ID        = /\{moz:distributionID\}/g;
+const MOZ_PARAM_OFFICIAL       = /\{moz:official\}/g;
+
+// Custom search parameters
+#ifdef MOZ_OFFICIAL_BRANDING
+const MOZ_OFFICIAL = "official";
+#else
+const MOZ_OFFICIAL = "unofficial";
+#endif
+
+#if MOZ_UPDATE_CHANNEL == beta
+const GOOGLE_CLIENT = "firefox-beta";
+#elif MOZ_UPDATE_CHANNEL == aurora
+const GOOGLE_CLIENT = "firefox-aurora";
+#elif MOZ_UPDATE_CHANNEL == nightly
+const GOOGLE_CLIENT = "firefox-nightly";
+#else
+const GOOGLE_CLIENT = "firefox-a";
+#endif
+
+#expand const MOZ_DISTRIBUTION_ID = __MOZ_DISTRIBUTION_ID__;
+
+function getLocale() {
+  const localePref = "general.useragent.locale";
+  return getLocalizedPref(localePref, Services.prefs.getCharPref(localePref));
+}
+
+function getLocalizedPref(aPrefName, aDefault) {
+  try {
+    return Services.prefs.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
+  } catch (ex) {
+    return aDefault;
+  }
+
+  return aDefault;
+}
+
+function test() {
+  let engine = Services.search.getEngineByName("Google");
+  ok(engine, "Google is installed");
+
+  is(Services.search.defaultEngine, engine, "Check that Google is the default search engine");
+
+  let distributionID;
+  try {
+    distributionID = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "distributionID");
+  } catch (ex) {
+    distributionID = MOZ_DISTRIBUTION_ID;
+  }
+
+  let base = "https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&aq=t&rls={moz:distributionID}:{moz:locale}:{moz:official}&client=" + GOOGLE_CLIENT;
+  base = base.replace(MOZ_PARAM_LOCALE, getLocale());
+  base = base.replace(MOZ_PARAM_DIST_ID, distributionID);
+  base = base.replace(MOZ_PARAM_OFFICIAL, MOZ_OFFICIAL);
+
+  let url;
+
+  // Test search URLs (including purposes).
+  url = engine.getSubmission("foo").uri.spec;
+  is(url, base, "Check search URL for 'foo'");
+
+  waitForExplicitFinish();
+
+  var gCurrTest;
+  var gTests = [
+    {
+      name: "context menu search",
+      searchURL: base + "&channel=rcs",
+      run: function () {
+        // Simulate a contextmenu search
+        // FIXME: This is a bit "low-level"...
+        BrowserSearch.loadSearch("foo", false, "contextmenu");
+      }
+    },
+    {
+      name: "keyword search",
+      searchURL: base + "&channel=fflb",
+      run: function () {
+        gURLBar.value = "? foo";
+        gURLBar.focus();
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "search bar search",
+      searchURL: base + "&channel=sb",
+      run: function () {
+        let sb = BrowserSearch.searchBar;
+        sb.focus();
+        sb.value = "foo";
+        registerCleanupFunction(function () {
+          sb.value = "";
+        });
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "home page search",
+      searchURL: base + "&channel=np&source=hp",
+      run: function () {
+        // load about:home, but remove the listener first so it doesn't
+        // get in the way
+        gBrowser.removeProgressListener(listener);
+        gBrowser.loadURI("about:home");
+        info("Waiting for about:home load");
+        tab.linkedBrowser.addEventListener("load", function load(event) {
+          if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+              event.target.location.href == "about:blank") {
+            info("skipping spurious load event");
+            return;
+          }
+          tab.linkedBrowser.removeEventListener("load", load, true);
+
+          // Observe page setup
+          let doc = gBrowser.contentDocument;
+          let mutationObserver = new MutationObserver(function (mutations) {
+            for (let mutation of mutations) {
+              if (mutation.attributeName == "searchEngineName") {
+                // Re-add the listener, and perform a search
+                gBrowser.addProgressListener(listener);
+                doc.getElementById("searchText").value = "foo";
+                doc.getElementById("searchSubmit").click();
+              }
+            }
+          });
+          mutationObserver.observe(doc.documentElement, { attributes: true });
+        }, true);
+      }
+    }
+  ];
+
+  function nextTest() {
+    if (gTests.length) {
+      gCurrTest = gTests.shift();
+      info("Running : " + gCurrTest.name);
+      executeSoon(gCurrTest.run);
+    } else {
+      finish();
+    }
+  }
+
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+
+  let listener = {
+    onStateChange: function onStateChange(webProgress, req, flags, status) {
+      info("onStateChange");
+      // Only care about top-level document starts
+      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+                     Ci.nsIWebProgressListener.STATE_START;
+      if (!(flags & docStart) || !webProgress.isTopLevel)
+        return;
+
+      info("received document start");
+
+      ok(req instanceof Ci.nsIChannel, "req is a channel");
+      is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded");
+      info("Actual URI: " + req.URI.spec);
+
+      req.cancel(Components.results.NS_ERROR_FAILURE);
+
+      executeSoon(nextTest);
+    }
+  }
+
+  registerCleanupFunction(function () {
+    gBrowser.removeProgressListener(listener);
+    gBrowser.removeTab(tab);
+  });
+
+  tab.linkedBrowser.addEventListener("load", function load() {
+    tab.linkedBrowser.removeEventListener("load", load, true);
+    gBrowser.addProgressListener(listener);
+    nextTest();
+  }, true);
+}
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -20,13 +20,14 @@
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
 #elif MOZ_UPDATE_CHANNEL == nightly
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
 #else
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
 #endif
   <MozParam name="channel" condition="purpose" purpose="contextmenu" value="rcs"/>
   <MozParam name="channel" condition="purpose" purpose="keyword" value="fflb"/>
+  <MozParam name="channel" condition="purpose" purpose="searchbar" value="sb"/>
   <MozParam name="channel" condition="purpose" purpose="homepage" value="np"/>
   <MozParam name="source" condition="purpose" purpose="homepage" value="hp"/>
 </Url>
 <SearchForm>https://www.google.com/</SearchForm>
 </SearchPlugin>
--- a/browser/metro/base/content/Site.js
+++ b/browser/metro/base/content/Site.js
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 /**
  * dumb model class to provide default values for sites.
  * link parameter/model object expected to have a .url property, and optionally .title
  */
 function Site(aLink) {
-  if(!aLink.url) {
+  if (!aLink.url) {
     throw Cr.NS_ERROR_INVALID_ARG;
   }
   this._link = aLink;
 }
 
 Site.prototype = {
   icon: '',
   get url() {
@@ -59,16 +59,16 @@ Site.prototype = {
     for (let key in attrs) {
       if (undefined === attrs[key]) {
         aNode.removeAttribute(key);
       } else {
         aNode.setAttribute(key, attrs[key]);
       }
     }
     // is binding already applied?
-    if (aNode.refresh) {
+    if ('refresh' in aNode) {
       // just update it
       aNode.refresh();
     } else {
       // these attribute values will get picked up later when the binding is applied
     }
   }
 };
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -22,17 +22,17 @@
       <property name="_grid" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'grid');"/>
 
       <property name="isBound" readonly="true" onget="return !!this._grid"/>
       <property name="isArranging" readonly="true" onget="return !!this._scheduledArrangeItemsTimerId"/>
 
       <field name="controller">null</field>
 
       <!-- collection of child items excluding empty tiles -->
-      <property name="items" readonly="true" onget="return this.querySelectorAll('richgriditem');"/>
+      <property name="items" readonly="true" onget="return this.querySelectorAll('richgriditem[value]');"/>
       <property name="itemCount" readonly="true" onget="return this.items.length;"/>
 
       <!-- nsIDOMXULMultiSelectControlElement (not fully implemented) -->
 
       <method name="clearSelection">
         <body>
           <![CDATA[
             // 'selection' and 'selected' are confusingly overloaded here
@@ -91,17 +91,17 @@
         </body>
       </method>
 
       <method name="handleItemClick">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            if(!this.isBound)
+            if (!this.isBound)
               return;
 
             if ("single" == this.getAttribute("seltype")) {
               // we'll republish this as a selectionchange event on the grid
               aEvent.stopPropagation();
               this.selectItem(aItem);
             }
 
@@ -111,17 +111,17 @@
         </body>
       </method>
 
       <method name="handleItemContextMenu">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            if(!this.isBound || this.suppressOnSelect)
+            if (!this.isBound || this.suppressOnSelect)
               return;
             // we'll republish this as a selectionchange event on the grid
             aEvent.stopPropagation();
             this.toggleItemSelection(aItem);
           ]]>
         </body>
       </method>
 
@@ -170,17 +170,17 @@
           ]]>
         </setter>
       </property>
 
       <!-- partial implementation of multiple selection interface -->
       <property name="selectedItems">
         <getter>
           <![CDATA[
-            return this.querySelectorAll("richgriditem[selected]");
+            return this.querySelectorAll("richgriditem[value][selected]");
           ]]>
         </getter>
       </property>
 
       <property name="selectedIndex">
         <getter>
           <![CDATA[
             return this.getIndexOfItem(this._selectedItem);
@@ -199,58 +199,146 @@
       </property>
 
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
-            let addition = this._createItemElement(aLabel, aValue);
-            this.appendChild(addition);
+            let item = this.nextSlot();
+            item.setAttribute("value", aValue);
+            item.setAttribute("label", aLabel);
+
             if (!aSkipArrange)
               this.arrangeItems();
-            return addition;
+            return item;
           ]]>
         </body>
       </method>
 
+      <method name="_slotValues">
+        <body><![CDATA[
+          return Array.map(this.children, (cnode) => cnode.getAttribute("value"));
+        ]]></body>
+      </method>
+
+      <property name="minSlots" readonly="true"
+                onget="return this.getAttribute('minSlots') || 3;"/>
+
       <method name="clearAll">
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
-            while (this.firstChild) {
-              this.removeChild(this.firstChild);
+            const ELEMENT_NODE_TYPE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
+            let slotCount = this.minSlots;
+            let childIndex = 0;
+            let child = this.firstChild;
+            while (child) {
+              // remove excess elements and non-element nodes
+              if (child.nodeType !== ELEMENT_NODE_TYPE || childIndex+1 > slotCount) {
+                let orphanNode = child;
+                child = orphanNode.nextSibling;
+                this.removeChild(orphanNode);
+                continue;
+              }
+              if (child.hasAttribute("value")) {
+                this._releaseSlot(child);
+              }
+              child = child.nextSibling;
+              childIndex++;
             }
+            // create our quota of item slots
+            for (let count = this.childElementCount; count < slotCount; count++) {
+              this.appendChild( this._createItemElement() );
+            }
+
             if (!aSkipArrange)
               this.arrangeItems();
           ]]>
         </body>
       </method>
 
+      <method name="_slotAt">
+        <parameter name="anIndex"/>
+        <body>
+          <![CDATA[
+            // backfill with new slots as necessary
+            let count = Math.max(1+anIndex, this.minSlots) - this.childElementCount;
+            for (; count > 0; count--) {
+              this.appendChild( this._createItemElement() );
+            }
+            return this.children[anIndex];
+          ]]>
+        </body>
+      </method>
+
+      <method name="nextSlot">
+        <body>
+          <![CDATA[
+            if (!this.itemCount) {
+              return this._slotAt(0);
+            }
+            let lastItem = this.items[this.itemCount-1];
+            let nextIndex = 1 + Array.indexOf(this.children, lastItem);
+            return this._slotAt(nextIndex);
+          ]]>
+        </body>
+      </method>
+
+      <method name="_releaseSlot">
+        <parameter name="anItem"/>
+        <body>
+          <![CDATA[
+            // Flush out data and state attributes so we can recycle this slot/element
+            let exclude = { value: 1, tiletype: 1 };
+            let attrNames = [attr.name for (attr of anItem.attributes)];
+            for (let attrName of attrNames) {
+              if (!(attrName in exclude))
+                anItem.removeAttribute(attrName);
+            }
+            // clear out inline styles
+            anItem.removeAttribute("style");
+            // finally clear the value, which should apply the richgrid-empty-item binding
+            anItem.removeAttribute("value");
+          ]]>
+        </body>
+      </method>
+
       <method name="insertItemAt">
         <parameter name="anIndex"/>
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
+            anIndex = Math.min(this.itemCount, anIndex);
+            let insertedItem;
             let existing = this.getItemAtIndex(anIndex);
-            let addition = this._createItemElement(aLabel, aValue);
             if (existing) {
-              this.insertBefore(addition, existing);
-            } else {
-              this.appendChild(addition);
+              // use an empty slot if we have one, otherwise insert it
+              let childIndex = Array.indexOf(this.children, existing);
+              if (childIndex > 0 && !this.children[childIndex-1].hasAttribute("value")) {
+                insertedItem = this.children[childIndex-1];
+              } else {
+                insertedItem = this.insertBefore(this._createItemElement(),existing);
+              }
             }
+            if (!insertedItem) {
+              insertedItem = this._slotAt(anIndex);
+            }
+            insertedItem.setAttribute("value", aValue);
+            insertedItem.setAttribute("label", aLabel);
             if (!aSkipArrange)
               this.arrangeItems();
-            return addition;
+            return insertedItem;
           ]]>
         </body>
       </method>
+
       <method name="removeItemAt">
         <parameter name="anIndex"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
             let item = this.getItemAtIndex(anIndex);
             if (!item)
               return null;
@@ -261,17 +349,23 @@
 
       <method name="removeItem">
         <parameter name="aItem"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
             if (!aItem || Array.indexOf(this.items, aItem) < 0)
               return null;
+
             let removal = this.removeChild(aItem);
+            // replace the slot if necessary
+            if (this.childElementCount < this.minSlots) {
+              this.nextSlot();
+            }
+
             if (removal && !aSkipArrange)
                 this.arrangeItems();
 
             // note that after removal the node is unbound
             // so none of the richgriditem binding methods & properties are available
             return removal;
           ]]>
         </body>
@@ -417,16 +511,17 @@
             return true;
           ]]>
         </getter>
       </property>
 
       <field name="_scheduledArrangeItemsTimerId">null</field>
       <field name="_scheduledArrangeItemsTries">0</field>
       <field name="_maxArrangeItemsRetries">5</field>
+
       <method name="_scheduleArrangeItems">
         <parameter name="aTime"/>
         <body>
           <![CDATA[
               // cap the number of times we reschedule calling arrangeItems
               if (
                   !this._scheduledArrangeItemsTimerId &&
                   this._maxArrangeItemsRetries > this._scheduledArrangeItemsTries
@@ -448,50 +543,51 @@
             if (!this._canLayout) {
               // try again later
               this._scheduleArrangeItems();
               return;
             }
 
             let itemDims = this._itemSize;
             let containerDims = this._containerSize;
+            let slotsCount = this.childElementCount;
 
             // reset the flags
             if (this._scheduledArrangeItemsTimerId) {
               clearTimeout(this._scheduledArrangeItemsTimerId);
               delete this._scheduledArrangeItemsTimerId;
             }
             this._scheduledArrangeItemsTries = 0;
 
             // clear explicit width and columns before calculating from avail. height again
             let gridStyle = this._grid.style;
             gridStyle.removeProperty("min-width");
             gridStyle.removeProperty("-moz-column-count");
 
             if (this.hasAttribute("vertical")) {
               this._columnCount = Math.floor(containerDims.width / itemDims.width) || 1;
-              this._rowCount = Math.floor(this.itemCount / this._columnCount);
+              this._rowCount = Math.floor(slotsCount / this._columnCount);
             } else {
-              // We favor overflowing horizontally, not vertically (rows then colums)
-              // rows attribute = max rows
-              let maxRowCount = Math.min(this.getAttribute("rows") || Infinity, Math.floor(containerDims.height / itemDims.height));
-              this._rowCount = Math.min(this.itemCount, maxRowCount);
+              // rows attribute is fixed number of rows
+              let maxRows = Math.floor(containerDims.height / itemDims.height);
+              this._rowCount = this.getAttribute("rows") ?
+                                  // fit indicated rows when possible
+                                  Math.min(maxRows, this.getAttribute("rows")) :
+                                  // at least 1 row
+                                  Math.min(maxRows, slotsCount) || 1;
 
-              // columns attribute = min cols
-              this._columnCount = this.itemCount ?
-                    Math.max(
-                        // at least 1 column when there are items
-                        this.getAttribute("columns") || 1,
-                        Math.ceil(this.itemCount / this._rowCount)
-                    ) : this.getAttribute("columns") || 0;
+              // columns attribute is min number of cols
+              this._columnCount = Math.ceil(slotsCount / this._rowCount) || 1;
+              if (this.getAttribute("columns")) {
+                this._columnCount = Math.max(this._columnCount, this.getAttribute("columns"));
+              }
             }
 
             // width is typically auto, cap max columns by truncating items collection
             // or, setting max-width style property with overflow hidden
-            // '0' is an invalid value, just leave the property unset when 0 columns
             if (this._columnCount) {
               gridStyle.MozColumnCount = this._columnCount;
             }
             this._fireEvent("arranged");
           ]]>
         </body>
       </method>
       <method name="arrangeItemsNow">
@@ -516,16 +612,22 @@
                   onset="this.setAttribute('suppressonselect', val);"/>
       <property name="crossSlideBoundary"
           onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
 
     <!-- Internal methods -->
       <field name="_xslideHandler"/>
       <constructor>
         <![CDATA[
+          // create our quota of item slots
+          for (let count = this.childElementCount, slotCount = this.minSlots;
+              count < slotCount; count++) {
+            this.appendChild( this._createItemElement() );
+          }
+
           if (this.controller && this.controller.gridBoundCallback != undefined)
             this.controller.gridBoundCallback();
 
           // set up cross-slide gesture handling for multiple-selection grids
           if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
             this._xslideHandler = new CrossSlide.Handler(this, {
                   REARRANGESTART: this.crossSlideBoundary
             });
@@ -600,16 +702,17 @@
               }
             } else {
               throw new Error("Failed to find stylesheet to parse out richgriditem dimensions\n");
             }
             return typeSizes;
           ]]>
         </body>
       </method>
+
       <method name="_isIndexInBounds">
         <parameter name="anIndex"/>
         <body>
           <![CDATA[
             return anIndex >= 0 && anIndex < this.itemCount;
           ]]>
         </body>
       </method>
@@ -621,17 +724,17 @@
           <![CDATA[
             let item = this.ownerDocument.createElement("richgriditem");
             if (aValue) {
               item.setAttribute("value", aValue);
             }
             if (aLabel) {
               item.setAttribute("label", aLabel);
             }
-            if(this.hasAttribute("tiletype")) {
+            if (this.hasAttribute("tiletype")) {
               item.setAttribute("tiletype", this.getAttribute("tiletype"));
             }
             return item;
           ]]>
         </body>
       </method>
 
       <method name="_fireEvent">
@@ -699,16 +802,17 @@
               bendNode.style.transform = "perspective("+perspective+") rotateY(" + angle + "deg)";
               bendNode.style.transformOrigin = "right center";
             }
           }
           // mark when bend effect is applied
           aItem.setAttribute("bending", true);
         ]]></body>
       </method>
+
       <method name="unbendItem">
         <parameter name="aItem"/>
         <body><![CDATA[
           // clear the 'bend' transform on the contentBox element of the item
           let bendNode = 'richgriditem' == aItem.nodeName && aItem._contentBox;
           if (bendNode && aItem.hasAttribute("bending")) {
             bendNode.style.removeProperty('transform');
             bendNode.style.removeProperty('transformOrigin');
@@ -744,17 +848,17 @@
       <handler event="MozCrossSliding">
         <![CDATA[
           // MozCrossSliding is swipe gesture across a tile
           // The tile should follow the drag to reinforce the gesture
           // (with inertia/speedbump behavior)
           let state = event.crossSlidingState;
           let thresholds = this._xslideHandler.thresholds;
           let transformValue;
-          switch(state) {
+          switch (state) {
             case "cancelled":
               this.unbendItem(event.target);
               event.target.removeAttribute('crosssliding');
               // hopefully nothing else is transform-ing the tile
               event.target.style.removeProperty('transform');
               break;
             case "dragging":
             case "selecting":
@@ -839,17 +943,17 @@
       <property name="pinned"
                 onget="return this.hasAttribute('pinned')"
                 onset="if (val) { this.setAttribute('pinned', val) } else this.removeAttribute('pinned');"/>
 
       <method name="refresh">
         <body>
           <![CDATA[
             // Prevent an exception in case binding is not done yet.
-            if(!this.isBound)
+            if (!this.isBound)
               return;
 
             // Seed the binding properties from bound-node attribute values
             // Usage: node.refresh()
             //        - reinitializes all binding properties from their associated attributes
 
             this.iconSrc = this.getAttribute('iconURI');
             this.color = this.getAttribute("customColor");
@@ -909,30 +1013,30 @@
             this.removeAttribute("customImage");
             this._top.style.removeProperty("background-image");
           }
         ]]></setter>
       </property>
 
       <method name="refreshBackgroundImage">
         <body><![CDATA[
-          if(!this.isBound)
+          if (!this.isBound)
             return;
           if (this.backgroundImage) {
             this._top.style.removeProperty("background-image");
             this._top.style.setProperty("background-image", this.backgroundImage);
           }
         ]]></body>
       </method>
 
       <field name="_contextActions">null</field>
       <property name="contextActions">
         <getter>
           <![CDATA[
-            if(!this._contextActions) {
+            if (!this._contextActions) {
               this._contextActions = new Set();
               let actionSet = this._contextActions;
               let actions = this.getAttribute("data-contextactions");
               if (actions) {
                 actions.split(/[,\s]+/).forEach(function(verb){
                   actionSet.add(verb);
                 });
               }
@@ -954,17 +1058,23 @@
         ]]>
       </handler>
 
       <handler event="contextmenu">
         <![CDATA[
           // fires for right-click, long-click and (keyboard) contextmenu input
           // toggle the selected state of tiles in a grid
           let gridParent = this.control;
-          if(!this.isBound || !gridParent)
+          if (!this.isBound || !gridParent)
             return;
           gridParent.handleItemContextMenu(this, event);
         ]]>
       </handler>
     </handlers>
   </binding>
 
+  <binding id="richgrid-empty-item">
+    <content>
+      <html:div anonid="anon-tile" class="tile-content"></html:div>
+    </content>
+  </binding>
+
 </bindings>
--- a/browser/metro/base/content/browser.css
+++ b/browser/metro/base/content/browser.css
@@ -114,16 +114,19 @@ setting[type="menulist"] {
   -moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar-autocomplete");
 }
 
 richgrid {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid");
 }
 
 richgriditem {
+  -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-empty-item");
+}
+richgriditem[value] {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-item");
 }
 
 placeitem {
   -moz-binding: url("chrome://browser/content/bindings/bindings.xml#place-item");
   background-color: transparent;
 }
 
--- a/browser/metro/base/content/startui/BookmarksView.js
+++ b/browser/metro/base/content/startui/BookmarksView.js
@@ -68,21 +68,21 @@ BookmarksView.prototype = Util.extend(Ob
   },
 
   handleItemClick: function bv_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
-    return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']");
+    return this._set.querySelector("richgriditem[anonid='" + aBookmarkId + "']");
   },
 
   _getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) {
-    return +aItem.getAttribute("bookmarkId");
+    return +aItem.getAttribute("anonid");
   },
 
   _updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
     for (let name in aAttrs)
       anItem.setAttribute(name, aAttrs[name]);
   },
 
   getBookmarks: function bv_getBookmarks(aRefresh) {
@@ -137,37 +137,39 @@ BookmarksView.prototype = Util.extend(Ob
 
     // Remove extra items in case a refresh added more than the limit.
     // This can happen when undoing a delete.
     if (aRefresh) {
       while (this._set.itemCount > limit)
         this._set.removeItemAt(this._set.itemCount - 1, true);
     }
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
     this._inBatch = false;
     rootNode.containerOpen = false;
   },
 
   inCurrentView: function bv_inCurrentView(aParentId, aItemId) {
     if (this._root && aParentId != this._root)
       return false;
 
     return !!this._getItemForBookmarkId(aItemId);
   },
 
   clearBookmarks: function bv_clearBookmarks() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   addBookmark: function bv_addBookmark(aBookmarkId, aPos) {
     let index = this._bookmarkService.getItemIndex(aBookmarkId);
     let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
     let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
     let item = this._set.insertItemAt(aPos || index, title, uri.spec, this._inBatch);
-    item.setAttribute("bookmarkId", aBookmarkId);
+    item.setAttribute("anonid", aBookmarkId);
     this._setContextActions(item);
     this._updateFavicon(item, uri);
   },
 
   _setContextActions: function bv__setContextActions(aItem) {
     let itemId = this._getBookmarkIdForItem(aItem);
     aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(itemId) ? "unpin" : "pin"));
     if (aItem.refresh) aItem.refresh();
@@ -193,16 +195,17 @@ BookmarksView.prototype = Util.extend(Ob
       this.removeBookmark(aBookmarkId);
       this.addBookmark(aBookmarkId);
       return;
     }
 
     let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
     let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
 
+    item.setAttribute("anonid", aBookmarkId);
     item.setAttribute("value", uri.spec);
     item.setAttribute("label", title);
 
     this._updateFavicon(item, uri);
   },
 
   removeBookmark: function bv_removeBookmark(aBookmarkId) {
     let item = this._getItemForBookmarkId(aBookmarkId);
--- a/browser/metro/base/content/startui/HistoryView.js
+++ b/browser/metro/base/content/startui/HistoryView.js
@@ -90,16 +90,17 @@ HistoryView.prototype = Util.extend(Obje
     // This can happen when undoing a delete.
     if (aRefresh) {
       while (this._set.itemCount > limit)
         this._set.removeItemAt(this._set.itemCount - 1);
     }
 
     rootNode.containerOpen = false;
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
     if (this._inBatch > 0)
       this._inBatch--;
   },
 
   addItemToSet: function addItemToSet(aURI, aTitle, aIcon, aPos) {
     let item = this._set.insertItemAt(aPos || 0, aTitle, aURI, this._inBatch);
     this._setContextActions(item);
     this._updateFavicon(item, aURI);
@@ -125,16 +126,19 @@ HistoryView.prototype = Util.extend(Obje
     if (!this._inBatch)
       this._set.arrangeItems();
   },
 
   doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) {
     let tileGroup = this._set;
     let selectedTiles = tileGroup.selectedItems;
 
+    // just arrange the grid once at the end of any action handling
+    this._inBatch = true;
+
     switch (aActionName){
       case "delete":
         Array.forEach(selectedTiles, function(aNode) {
           if (!this._toRemove) {
             this._toRemove = [];
           }
 
           let uri = aNode.getAttribute("value");
@@ -177,19 +181,21 @@ HistoryView.prototype = Util.extend(Obje
         Array.forEach(selectedTiles, function(aNode) {
           let uri = aNode.getAttribute("value");
 
           this._pinHelper.setPinned(uri);
         }, this);
         break;
 
       default:
+        this._inBatch = false;
         return;
     }
 
+    this._inBatch = false;
     // Send refresh event so all view are in sync.
     this._sendNeedsRefresh();
   },
 
   handleEvent: function bv_handleEvent(aEvent) {
     switch (aEvent.type){
       case "MozAppbarDismissing":
         // If undo wasn't pressed, time to do definitive actions.
@@ -249,27 +255,28 @@ HistoryView.prototype = Util.extend(Obje
     }
   },
 
   onDeleteURI: function(aURI) {
     this.removeHistory(aURI.spec);
   },
 
   onClearHistory: function() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   onPageChanged: function(aURI, aWhat, aValue) {
     if (aWhat ==  Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
       let changedItems = this._set.getItemsByUrl(aURI.spec);
       for (let item of changedItems) {
         let currIcon = item.getAttribute("iconURI");
         if (currIcon != aValue) {
           item.setAttribute("iconURI", aValue);
-          if("refresh" in item)
+          if ("refresh" in item)
             item.refresh();
         }
       }
     }
   },
 
   onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) {
     if ((aReason ==  Ci.nsINavHistoryObserver.REASON_DELETED) && !this._inBatch) {
--- a/browser/metro/base/content/startui/RemoteTabsView.js
+++ b/browser/metro/base/content/startui/RemoteTabsView.js
@@ -88,16 +88,17 @@ RemoteTabsView.prototype = Util.extend(O
 
         let item = this._set.appendItem((title || url), url);
         item.setAttribute("iconURI", Weave.Utils.getIcon(icon));
 
       }, this);
     }
     this.setUIAccessVisible(show);
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
   },
 
   destruct: function destruct() {
     Weave.Svc.Obs.remove("weave:engine:sync:finish", this);
     Weave.Svc.Obs.remove("weave:service:logout:start-over", this);
     View.prototype.destruct.call(this);
   },
 
--- a/browser/metro/base/content/startui/Start.xul
+++ b/browser/metro/base/content/startui/Start.xul
@@ -43,39 +43,61 @@
   <hbox id="start-container" class="meta-section-container"
         observes="bcast_windowState">
   <!-- the start-container element has a max-height set in StartUI.js -->
       <vbox id="start-topsites" class="meta-section" expanded="true">
         <label class="meta-section-title wide-title" value="&topSitesHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-topsites')">
           &narrowTopSitesHeader.label;
         </html:div>
-        <richgrid id="start-topsites-grid" observes="bcast_windowState" set-name="topSites" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" flex="1"/>
+        <richgrid id="start-topsites-grid" observes="bcast_windowState" set-name="topSites" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" minSlots="8" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
       <vbox id="start-bookmarks" class="meta-section">
         <label class="meta-section-title wide-title" value="&bookmarksHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-bookmarks')">
           &narrowBookmarksHeader.label;
         </html:div>
-        <richgrid id="start-bookmarks-grid" observes="bcast_windowState" set-name="bookmarks" seltype="multiple" flex="1"/>
+        <richgrid id="start-bookmarks-grid" observes="bcast_windowState" set-name="bookmarks" seltype="multiple" fade="true" flex="1" minSlots="2">
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
       <vbox id="start-history" class="meta-section">
         <label class="meta-section-title wide-title" value="&recentHistoryHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-history')">
           &narrowRecentHistoryHeader.label;
         </html:div>
-        <richgrid id="start-history-grid" observes="bcast_windowState" set-name="recentHistory" seltype="multiple" flex="1"/>
+        <richgrid id="start-history-grid" observes="bcast_windowState" set-name="recentHistory" seltype="multiple" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
 #ifdef MOZ_SERVICES_SYNC
       <vbox id="start-remotetabs" class="meta-section">
         <label class="meta-section-title wide-title" value="&remoteTabsHeader.label;"/>
         <html:div id="snappedRemoteTabsLabel" class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-remotetabs')">
           &narrowRemoteTabsHeader.label;
         </html:div>
-        <richgrid id="start-remotetabs-grid" observes="bcast_windowState" set-name="remoteTabs" seltype="multiple" flex="1"/>
+        <richgrid id="start-remotetabs-grid" observes="bcast_windowState" set-name="remoteTabs" seltype="multiple" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
+
       </vbox>
 #endif
   </hbox>
   </html:body>
 </html:html>
--- a/browser/metro/base/content/startui/TopSitesView.js
+++ b/browser/metro/base/content/startui/TopSitesView.js
@@ -153,16 +153,19 @@ TopSitesView.prototype = Util.extend(Obj
         // flush, recreate all
       this.isUpdating = true;
       // destroy and recreate all item nodes, skip calling arrangeItems
       this.populateGrid();
     }
   },
 
   updateTile: function(aTileNode, aSite, aArrangeGrid) {
+    if (!(aSite && aSite.url)) {
+      throw new Error("Invalid Site object passed to TopSitesView updateTile");
+    }
     this._updateFavicon(aTileNode, Util.makeURI(aSite.url));
 
     Task.spawn(function() {
       let filepath = PageThumbsStorage.getFilePathForURL(aSite.url);
       if (yield OS.File.exists(filepath)) {
         aSite.backgroundImage = 'url("'+PageThumbs.getThumbnailURL(aSite.url)+'")';
         if ('backgroundImage' in aTileNode) {
           aTileNode.backgroundImage = aSite.backgroundImage;
@@ -187,24 +190,21 @@ TopSitesView.prototype = Util.extend(Obj
     let sites = TopSites.getSites();
     if (this._topSitesMax) {
       sites = sites.slice(0, this._topSitesMax);
     }
     let tileset = this._set;
     tileset.clearAll(true);
 
     for (let site of sites) {
-      // call to private _createItemElement is a temp measure
-      // we'll eventually just request the next slot
-      let item = tileset._createItemElement(site.title, site.url);
-
-      this.updateTile(item, site);
-      tileset.appendChild(item);
+      let slot = tileset.nextSlot();
+      this.updateTile(slot, site);
     }
     tileset.arrangeItems();
+    tileset.removeAttribute("fade");
     this.isUpdating = false;
   },
 
   forceReloadOfThumbnail: function forceReloadOfThumbnail(url) {
     let nodes = this._set.querySelectorAll('richgriditem[value="'+url+'"]');
     for (let item of nodes) {
       if ("isBound" in item && item.isBound) {
         item.refreshBackgroundImage();
@@ -239,17 +239,17 @@ TopSitesView.prototype = Util.extend(Obj
       } else {
         item.removeAttribute("tiletype");
       }
     }
   },
 
   // nsIObservers
   observe: function (aSubject, aTopic, aState) {
-    switch(aTopic) {
+    switch (aTopic) {
       case "Metro:RefreshTopsiteThumbnail":
         this.forceReloadOfThumbnail(aState);
         break;
     }
   },
 
   // nsINavHistoryObserver
   onBeginUpdateBatch: function() {
@@ -264,17 +264,18 @@ TopSitesView.prototype = Util.extend(Obj
 
   onTitleChanged: function(aURI, aPageTitle) {
   },
 
   onDeleteURI: function(aURI) {
   },
 
   onClearHistory: function() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   onPageChanged: function(aURI, aWhat, aValue) {
   },
 
   onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) {
   },
 
--- a/browser/metro/base/tests/mochitest/browser_history.js
+++ b/browser/metro/base/tests/mochitest/browser_history.js
@@ -62,17 +62,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     unpinButton.click();
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(2)), "Item unpinned");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
 
     // --------- unpin multiple items
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -119,17 +119,17 @@ gTests.push({
 
     let promise = waitForCondition(() => !restoreButton.hidden);
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
-    ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not deleted yet");
+    ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not actually deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
     ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
@@ -145,34 +145,41 @@ gTests.push({
     sendContextMenuClickToElement(window, item, 10, 10);
     yield promise;
 
     yield waitForCondition(() => !deleteButton.hidden);
 
     ok(!deleteButton.hidden, "Delete button is visible.");
 
     let promise = waitForCondition(() => !restoreButton.hidden);
+    let populateGridSpy = spyOnMethod(gStartView, "populateGrid");
+
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
+    is(populateGridSpy.callCount, 1, "populateGrid was called in response to the deleting a tile");
+
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
+    is(populateGridSpy.callCount, 1, "populateGrid not called when a removed item is actually deleted");
+    populateGridSpy.restore();
+
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(2)], "Item RIP");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
--- a/browser/metro/base/tests/mochitest/browser_tilegrid.xul
+++ b/browser/metro/base/tests/mochitest/browser_tilegrid.xul
@@ -12,26 +12,29 @@
 <!DOCTYPE window []>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <vbox id="alayout">
       <richgrid id="grid_layout" seltype="single" flex="1">
       </richgrid>
   </vbox>
+  <vbox>
+      <richgrid id="slots_grid" seltype="single" minSlots="6" flex="1"/>
+  </vbox>
   <vbox style="height:600px">
     <hbox>
       <richgrid id="clearGrid" seltype="single" flex="1" rows="2">
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
         <richgriditem value="about:blank" id="clearGrid_item2" label="2nd item"/>
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
       </richgrid>
     </hbox>
     <hbox>
-      <richgrid id="emptyGrid" seltype="single" flex="1" rows="2">
+      <richgrid id="emptyGrid" seltype="single" flex="1" rows="2" minSlots="6">
       </richgrid>
     </hbox>
     <hbox>
       <richgrid id="grid1" seltype="single" flex="1">
         <richgriditem value="about:blank" id="grid1_item1" label="First item"/>
         <richgriditem value="about:blank" id="grid1_item2" label="2nd item"/>
       </richgrid>
     </hbox>
--- a/browser/metro/base/tests/mochitest/browser_tiles.js
+++ b/browser/metro/base/tests/mochitest/browser_tiles.js
@@ -4,27 +4,35 @@ function test() {
   waitForExplicitFinish();
   Task.spawn(function(){
     info(chromeRoot + "browser_tilegrid.xul");
     yield addTab(chromeRoot + "browser_tilegrid.xul");
     doc = Browser.selectedTab.browser.contentWindow.document;
   }).then(runTests);
 }
 
+function _checkIfBoundByRichGrid_Item(expected, node, idx) {
+  let binding = node.ownerDocument.defaultView.getComputedStyle(node).MozBinding;
+  let result = ('url("chrome://browser/content/bindings/grid.xml#richgrid-item")' == binding);
+  return (result == expected);
+}
+let isBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, true);
+let isNotBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, false);
+
 gTests.push({
   desc: "richgrid binding is applied",
   run: function() {
     ok(doc, "doc got defined");
 
     let grid = doc.querySelector("#grid1");
     ok(grid, "#grid1 is found");
     is(typeof grid.clearSelection, "function", "#grid1 has the binding applied");
-
     is(grid.items.length, 2, "#grid1 has a 2 items");
     is(grid.items[0].control, grid, "#grid1 item's control points back at #grid1'");
+    ok(Array.every(grid.items, isBoundByRichGrid_Item), "All items are bound by richgrid-item");
   }
 });
 
 gTests.push({
   desc: "item clicks are handled",
   run: function() {
     let grid = doc.querySelector("#grid1");
     is(typeof grid.handleItemClick, "function", "grid.handleItemClick is a function");
@@ -120,29 +128,38 @@ gTests.push({
     is(arrangeSpy.callCount, 1, "arrangeItems is called once when we clearAll");
     arrangeSpy.restore();
   }
 });
 
 gTests.push({
   desc: "empty grid",
   run: function() {
+    // XXX grids have minSlots and may not be ever truly empty
+
     let grid = doc.getElementById("emptyGrid");
     grid.arrangeItems();
     yield waitForCondition(() => !grid.isArranging);
 
-    // grid has rows=2 but 0 items
+    // grid has 2 rows, 6 slots, 0 items
     ok(grid.isBound, "binding was applied");
     is(grid.itemCount, 0, "empty grid has 0 items");
-    is(grid.rowCount, 0, "empty grid has 0 rows");
-    is(grid.columnCount, 0, "empty grid has 0 cols");
+    // minSlots attr. creates unpopulated slots
+    is(grid.rowCount, grid.getAttribute("rows"), "empty grid with rows-attribute has that number of rows");
+    is(grid.columnCount, 3, "empty grid has expected number of columns");
 
-    let columnsNode = grid._grid;
-    let cStyle = doc.defaultView.getComputedStyle(columnsNode);
-    is(cStyle.getPropertyValue("-moz-column-count"), "auto", "empty grid has -moz-column-count: auto");
+    // remove rows attribute and allow space for the grid to find its own height
+    // for its number of slots
+    grid.removeAttribute("rows");
+    grid.parentNode.style.height = 20+(grid.tileHeight*grid.minSlots)+"px";
+
+    grid.arrangeItems();
+    yield waitForCondition(() => !grid.isArranging);
+    is(grid.rowCount, grid.minSlots, "empty grid has this.minSlots rows");
+    is(grid.columnCount, 1, "empty grid has 1 column");
   }
 });
 
 gTests.push({
   desc: "appendItem",
   run: function() {
      // implements an appendItem with signature title, uri, returns item element
      // appendItem triggers arrangeItems
@@ -206,26 +223,35 @@ gTests.push({
      // implements an insertItemAt method, with index, title, uri.spec signature
      // insertItemAt triggers arrangeItems
     let grid = doc.querySelector("#grid3");
 
     is(grid.itemCount, 2, "2 items initially");
     is(typeof grid.insertItemAt, "function", "insertItemAt is a function on the grid");
 
     let arrangeStub = stubMethod(grid, "arrangeItems");
-    let insertedItem = grid.insertItemAt(1, "inserted item", "http://example.com/inserted");
+    let insertedAt0 = grid.insertItemAt(0, "inserted item 0", "http://example.com/inserted0");
+    let insertedAt00 = grid.insertItemAt(0, "inserted item 00", "http://example.com/inserted00");
+
+    ok(insertedAt0 && insertedAt00, "insertItemAt gives back an item");
+
+    is(insertedAt0.getAttribute("label"), "inserted item 0", "insertItemAt creates item with the correct label");
+    is(insertedAt0.getAttribute("value"), "http://example.com/inserted0", "insertItemAt creates item with the correct url value");
 
-    ok(insertedItem, "insertItemAt gives back an item");
-    is(grid.items[1], insertedItem, "item is inserted at the correct index");
-    is(insertedItem.getAttribute("label"), "inserted item", "insertItemAt creates item with the correct label");
-    is(insertedItem.getAttribute("value"), "http://example.com/inserted", "insertItemAt creates item with the correct url value");
-    is(grid.items[2].getAttribute("id"), "grid3_item2", "following item ends up at the correct index");
-    is(grid.itemCount, 3, "itemCount is incremented when we insertItemAt");
+    is(grid.items[0], insertedAt00, "item is inserted at the correct index");
+    is(grid.children[0], insertedAt00, "first item occupies the first slot");
+    is(grid.items[1], insertedAt0, "item is inserted at the correct index");
+    is(grid.children[1], insertedAt0, "next item occupies the next slot");
 
-    is(arrangeStub.callCount, 1, "arrangeItems is called when we insertItemAt");
+    is(grid.items[2].getAttribute("label"), "First item", "Old first item is now at index 2");
+    is(grid.items[3].getAttribute("label"), "2nd item", "Old 2nd item is now at index 3");
+
+    is(grid.itemCount, 4, "itemCount is incremented when we insertItemAt");
+
+    is(arrangeStub.callCount, 2, "arrangeItems is called when we insertItemAt");
     arrangeStub.restore();
   }
 });
 
 gTests.push({
   desc: "getIndexOfItem",
   run: function() {
      // implements a getIndexOfItem method, with item (element) signature
@@ -412,8 +438,177 @@ gTests.push({
 
     is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item");
     is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
     is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
     handlerStub.restore();
     doc.defaultView.removeEventListener("selectionchange", handler, false);
   }
 });
+
+function gridSlotsSetup() {
+    let grid = this.grid = doc.createElement("richgrid");
+    grid.setAttribute("minSlots", 6);
+    doc.documentElement.appendChild(grid);
+    is(grid.ownerDocument, doc, "created grid in the expected document");
+}
+function gridSlotsTearDown() {
+    this.grid && this.grid.parentNode.removeChild(this.grid);
+}
+
+gTests.push({
+  desc: "richgrid slots init",
+  setUp: gridSlotsSetup,
+  run: function() {
+    let grid = this.grid;
+    // grid is initially populated with empty slots matching the minSlots attribute
+    is(grid.children.length, 6, "minSlots slots are created");
+    is(grid.itemCount, 0, "slots do not count towards itemCount");
+    ok(Array.every(grid.children, (node) => node.nodeName == 'richgriditem'), "slots have nodeName richgriditem");
+    ok(Array.every(grid.children, isNotBoundByRichGrid_Item), "slots aren't bound by the richgrid-item binding");
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid using slots for items",
+  setUp: gridSlotsSetup, // creates grid with minSlots = num. slots = 6
+  run: function() {
+    let grid = this.grid;
+    let numSlots = grid.getAttribute("minSlots");
+    is(grid.children.length, numSlots);
+    // adding items occupies those slots
+    for (let idx of [0,1,2,3,4,5,6]) {
+      let slot = grid.children[idx];
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+      if (idx < numSlots) {
+        is(grid.children.length, numSlots);
+        is(slot, item, "The same node is reused when an item is assigned to a slot");
+      } else {
+        is(typeof slot, 'undefined');
+        ok(item);
+        is(grid.children.length, grid.itemCount);
+      }
+    }
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid assign and release slots",
+  setUp: function(){
+    info("assign and release slots setUp");
+    this.grid = doc.getElementById("slots_grid");
+    this.grid.scrollIntoView();
+    let rect = this.grid.getBoundingClientRect();
+    info("slots grid at top: " + rect.top + ", window.pageYOffset: " + doc.defaultView.pageYOffset);
+  },
+  run: function() {
+    let grid = this.grid;
+    // start with 5 of 6 slots occupied
+    for (let idx of [0,1,2,3,4]) {
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+      item.setAttribute("id", "test_item_"+idx);
+    }
+    is(grid.itemCount, 5);
+    is(grid.children.length, 6); // see setup, where we init with 6 slots
+    let firstItem = grid.items[0];
+
+    ok(firstItem.ownerDocument, "item has ownerDocument");
+    is(doc, firstItem.ownerDocument, "item's ownerDocument is the document we expect");
+
+    is(firstItem, grid.children[0], "Item and assigned slot are one and the same");
+    is(firstItem.control, grid, "Item is bound and its .control points back at the grid");
+
+    // before releasing, the grid should be nofified of clicks on that slot
+    let testWindow = grid.ownerDocument.defaultView;
+
+    let rect = firstItem.getBoundingClientRect();
+    {
+      let handleStub = stubMethod(grid, 'handleItemClick');
+      // send click to item and wait for next tick;
+      sendElementTap(testWindow, firstItem);
+      yield waitForMs(0);
+
+      is(handleStub.callCount, 1, "handleItemClick was called when we clicked an item");
+      handleStub.restore();
+    }
+    // _releaseSlot is semi-private, we don't expect consumers of the binding to call it
+    // but want to be sure it does what we expect
+    grid._releaseSlot(firstItem);
+
+    is(grid.itemCount, 4, "Releasing a slot gives us one less item");
+    is(firstItem, grid.children[0],"Released slot is still the same node we started with");
+
+    // after releasing, the grid should NOT be nofified of clicks
+    {
+      let handleStub = stubMethod(grid, 'handleItemClick');
+      // send click to item and wait for next tick;
+      sendElementTap(testWindow, firstItem);
+      yield waitForMs(0);
+
+      is(handleStub.callCount, 0, "handleItemClick was NOT called when we clicked a released slot");
+      handleStub.restore();
+    }
+
+    ok(!firstItem.mozMatchesSelector("richgriditem[value]"), "Released slot doesn't match binding selector");
+    ok(isNotBoundByRichGrid_Item(firstItem), "Released slot is no longer bound");
+
+    waitForCondition(() => isNotBoundByRichGrid_Item(firstItem));
+    ok(true, "Slot eventually gets unbound");
+    is(firstItem, grid.children[0], "Released slot is still at expected index in children collection");
+
+    let firstSlot = grid.children[0];
+    firstItem = grid.insertItemAt(0, "New item 0", "about:blank");
+    ok(firstItem == grid.items[0], "insertItemAt 0 creates item at expected index");
+    ok(firstItem == firstSlot, "insertItemAt occupies the released slot with the new item");
+    is(grid.itemCount, 5);
+    is(grid.children.length, 6);
+    is(firstItem.control, grid,"Item is bound and its .control points back at the grid");
+
+    let nextSlotIndex = grid.itemCount;
+    let lastItem = grid.insertItemAt(9, "New item 9", "about:blank");
+    // Check we don't create sparse collection of items
+    is(lastItem, grid.children[nextSlotIndex], "Item is appended at the next index when an out of bounds index is provided");
+    is(grid.children.length, 6);
+    is(grid.itemCount, 6);
+
+    grid.appendItem("one more", "about:blank");
+    is(grid.children.length, 7);
+    is(grid.itemCount, 7);
+
+    // clearAll results in slots being emptied
+    grid.clearAll();
+    is(grid.children.length, 6, "Extra slots are trimmed when we clearAll");
+    ok(!Array.some(grid.children, (node) => node.hasAttribute("value")), "All slots have no value attribute after clearAll")
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid slot management",
+  setUp: gridSlotsSetup,
+  run: function() {
+    let grid = this.grid;
+    // populate grid with some items
+    let numSlots = grid.getAttribute("minSlots");
+    for (let idx of [0,1,2,3,4,5]) {
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+    }
+
+    is(grid.itemCount, 6, "Grid setup with 6 items");
+    is(grid.children.length, 6, "Full grid has the expected number of slots");
+
+    // removing an item creates a replacement slot *on the end of the stack*
+    let item = grid.removeItemAt(0);
+    is(item.getAttribute("label"), "item 0", "removeItemAt gives back the populated node");
+    is(grid.children.length, 6);
+    is(grid.itemCount, 5);
+    is(grid.items[0].getAttribute("label"), "item 1", "removeItemAt removes the node so the nextSibling takes its place");
+    ok(grid.children[5] && !grid.children[5].hasAttribute("value"), "empty slot is added at the end of the existing children");
+
+    let item1 = grid.removeItem(grid.items[0]);
+    is(grid.children.length, 6);
+    is(grid.itemCount, 4);
+    is(grid.items[0].getAttribute("label"), "item 2", "removeItem removes the node so the nextSibling takes its place");
+  },
+  tearDown: gridSlotsTearDown
+});
--- a/browser/metro/theme/tiles.css
+++ b/browser/metro/theme/tiles.css
@@ -270,16 +270,36 @@ richgriditem[pinned]:-moz-locale-dir(rtl
 richgriditem[customColor] {
   color: #f1f1f1;
 }
 
 richgriditem[bending] > .tile-content {
   transform-origin: center center;
 }
 
+/* Empty/unused tiles */
+richgriditem:not([value]) {
+  visibility: hidden;
+}
+richgriditem[tiletype="thumbnail"]:not([value]) {
+  visibility: visible;
+}
+richgriditem:not([value]) > .tile-content {
+  padding: 10px 14px;
+}
+richgriditem[tiletype="thumbnail"]:not([value]) > .tile-content {
+  box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.05);
+  background-image: url("chrome://browser/skin/images/firefox-watermark.png");
+  background-origin: content-box;
+  background-repeat: no-repeat;
+  background-color: rgba(255,255,255, 0.2);
+  background-position: center center;
+  background-size: @grid_row_height@;
+}
+
 /* Snapped-view variation
    We use the compact, single-column grid treatment for <=320px */
 
 @media (max-width: 330px) {
 
   richgrid > .richgrid-grid {
     -moz-column-width: auto!important; /* let it flow */
     -moz-column-count: auto!important; /* let it flow */
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -164,17 +164,17 @@ let AboutHome = {
         } catch(ex) {
           Cu.reportError(ex);
           break;
         }
 #ifdef MOZ_SERVICES_HEALTHREPORT
         window.BrowserSearch.recordSearchInHealthReport(data.engineName, "abouthome");
 #endif
         // Trigger a search through nsISearchEngine.getSubmission()
-        let submission = Services.search.currentEngine.getSubmission(data.searchTerms);
+        let submission = Services.search.currentEngine.getSubmission(data.searchTerms, null, "homepage");
         window.loadURI(submission.uri.spec, null, submission.postData);
         break;
     }
   },
 
   // Send all the chrome-privileged data needed by about:home. This
   // gets re-sent when the search engine changes.
   sendAboutHomeData: function(target) {
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -4,21 +4,17 @@
 
 mobile-tests := mobile/android/base/tests
 TESTPATH     := $(topsrcdir)/$(mobile-tests)
 dir-tests    := $(DEPTH)/$(mobile-tests)
 
 ANDROID_APK_NAME := robocop-debug
 
 ANDROID_EXTRA_JARS += \
-  $(srcdir)/robotium-solo-4.2.jar \
-  $(NULL)
-
-ANDROID_RESFILES = \
-  res/values/strings.xml \
+  $(srcdir)/robotium-solo-4.3.jar \
   $(NULL)
 
 ANDROID_ASSETS_DIR := $(TESTPATH)/assets
 
 _JAVA_HARNESS := \
   Actions.java \
   Assert.java \
   Driver.java \
--- a/build/mobile/robocop/README
+++ b/build/mobile/robocop/README
@@ -1,12 +1,12 @@
 Robocop is a Mozilla project which uses Robotium to test Firefox on Android devices.
 
-Robotium is an open source tool licensed under the Apache 2.0 license and the original 
+Robotium is an open source tool licensed under the Apache 2.0 license and the original
 source can be found here:
 http://code.google.com/p/robotium/
 
-We are including robotium-solo-4.2.jar as a binary and are not modifying it in any way 
-from the original download found at: 
+We are including robotium-solo-4.3.jar as a binary and are not modifying it in any way
+from the original download found at:
 http://code.google.com/p/robotium/
 
 Firefox for Android developers should read the documentation in
 mobile/android/base/tests/README.rst.
--- a/build/mobile/robocop/moz.build
+++ b/build/mobile/robocop/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'robocop'
 
+ANDROID_RESFILES = [
+    'res/values/strings.xml',
+]
deleted file mode 100644
index 349f7516a0bf0d63a845577032429047073aeb72..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2bee9cb5f95ea5c4309640cf19fdced9fb2fa706
GIT binary patch
literal 106466
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%0Ax9;nLJLT
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
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -15,26 +15,16 @@ JAVAFILES = \
   Power.java \
   RedirOutputThread.java \
   RunCmdThread.java \
   RunDataThread.java \
   SUTAgentAndroid.java \
   WifiConfiguration.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable/icon.png \
-  res/drawable/ateamlogo.png \
-  res/drawable/ic_stat_first.png \
-  res/drawable/ic_stat_neterror.png \
-  res/drawable/ic_stat_warning.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 ANDROID_EXTRA_JARS = \
   $(srcdir)/network-libs/commons-net-2.0.jar \
   $(srcdir)/network-libs/jmdns.jar \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/fencp/Makefile.in
+++ b/build/mobile/sutagent/android/fencp/Makefile.in
@@ -6,19 +6,11 @@ ANDROID_APK_NAME := FenCP
 
 JAVAFILES = \
   DirCursor.java \
   FenCP.java \
   FenCPFP.java \
   FileCursor.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-mdpi/icon.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/fencp/moz.build
+++ b/build/mobile/sutagent/android/fencp/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'FenCP'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in
+++ b/build/mobile/sutagent/android/ffxcp/Makefile.in
@@ -6,19 +6,11 @@ ANDROID_APK_NAME := FfxCP
 
 JAVAFILES = \
   DirCursor.java \
   ffxcp.java \
   FfxCPFP.java \
   FileCursor.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-mdpi/icon.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/ffxcp/moz.build
+++ b/build/mobile/sutagent/android/ffxcp/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'FfxCP'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/moz.build
+++ b/build/mobile/sutagent/android/moz.build
@@ -1,8 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'sutAgentAndroid'
 
+ANDROID_RESFILES = [
+    'res/drawable/ateamlogo.png',
+    'res/drawable/ic_stat_first.png',
+    'res/drawable/ic_stat_neterror.png',
+    'res/drawable/ic_stat_warning.png',
+    'res/drawable/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/watcher/Makefile.in
+++ b/build/mobile/sutagent/android/watcher/Makefile.in
@@ -7,22 +7,11 @@ ANDROID_APK_NAME := Watcher
 JAVAFILES = \
   IWatcherService.java \
   RedirOutputThread.java \
   WatcherMain.java \
   WatcherReceiver.java \
   WatcherService.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-hdpi/ateamlogo.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-ldpi/ateamlogo.png \
-  res/drawable-mdpi/icon.png \
-  res/drawable-mdpi/ateamlogo.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/watcher/moz.build
+++ b/build/mobile/sutagent/android/watcher/moz.build
@@ -1,8 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'Watcher'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/ateamlogo.png',
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/ateamlogo.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/ateamlogo.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/config/makefiles/java-build.mk
+++ b/config/makefiles/java-build.mk
@@ -3,16 +3,17 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifndef INCLUDED_JAVA_BUILD_MK #{
 
 ifdef ANDROID_RESFILES #{
+ifndef IGNORE_ANDROID_RESFILES #{
 res-dep := .deps-copy-java-res
 
 GENERATED_DIRS += res
 GARBAGE        += $(res-dep)
 
 export:: $(res-dep)
 
 res-dep-preqs := \
@@ -20,16 +21,17 @@ res-dep-preqs := \
   $(call mkdir_deps,res) \
   $(if $(IS_LANGUAGE_REPACK),FORCE) \
   $(NULL)
 
 # nop-build: only copy res/ files when needed
 $(res-dep): $(res-dep-preqs)
 	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
 	@$(TOUCH) $@
+endif #} IGNORE_ANDROID_RESFILES
 endif #} ANDROID_RESFILES
 
 
 ifdef JAVAFILES #{
 GENERATED_DIRS += classes
 
 export:: classes
 classes: $(call mkdir_deps,classes)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -16,16 +16,18 @@ ifdef INCLUDED_RULES_MK
 endif
 INCLUDED_RULES_MK = 1
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
+  ANDROID_GENERATED_RESFILES \
+  ANDROID_RESFILES \
   CMMSRCS \
   CPP_UNIT_TESTS \
   DIRS \
   EXTRA_PP_COMPONENTS \
   EXTRA_PP_JS_MODULES \
   GTEST_CMMSRCS \
   GTEST_CPPSRCS \
   GTEST_CSRCS \
--- 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/js/src/config/makefiles/java-build.mk
+++ b/js/src/config/makefiles/java-build.mk
@@ -3,16 +3,17 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifndef INCLUDED_JAVA_BUILD_MK #{
 
 ifdef ANDROID_RESFILES #{
+ifndef IGNORE_ANDROID_RESFILES #{
 res-dep := .deps-copy-java-res
 
 GENERATED_DIRS += res
 GARBAGE        += $(res-dep)
 
 export:: $(res-dep)
 
 res-dep-preqs := \
@@ -20,16 +21,17 @@ res-dep-preqs := \
   $(call mkdir_deps,res) \
   $(if $(IS_LANGUAGE_REPACK),FORCE) \
   $(NULL)
 
 # nop-build: only copy res/ files when needed
 $(res-dep): $(res-dep-preqs)
 	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
 	@$(TOUCH) $@
+endif #} IGNORE_ANDROID_RESFILES
 endif #} ANDROID_RESFILES
 
 
 ifdef JAVAFILES #{
 GENERATED_DIRS += classes
 
 export:: classes
 classes: $(call mkdir_deps,classes)
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -16,16 +16,18 @@ ifdef INCLUDED_RULES_MK
 endif
 INCLUDED_RULES_MK = 1
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
+  ANDROID_GENERATED_RESFILES \
+  ANDROID_RESFILES \
   CMMSRCS \
   CPP_UNIT_TESTS \
   DIRS \
   EXTRA_PP_COMPONENTS \
   EXTRA_PP_JS_MODULES \
   GTEST_CMMSRCS \
   GTEST_CPPSRCS \
   GTEST_CSRCS \
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -500,17 +500,17 @@ abstract public class BrowserApp extends
         registerEventListener("Feedback:LastUrl");
         registerEventListener("Feedback:OpenPlayStore");
         registerEventListener("Feedback:MaybeLater");
         registerEventListener("Telemetry:Gather");
         registerEventListener("Settings:Show");
         registerEventListener("Updater:Launch");
         registerEventListener("Reader:GoToReadingList");
 
-        Distribution.init(this, getPackageResourcePath());
+        Distribution.init(this);
         JavaAddonManager.getInstance().init(getApplicationContext());
         mSharedPreferencesHelper = new SharedPreferencesHelper(getApplicationContext());
         mOrderedBroadcastHelper = new OrderedBroadcastHelper(getApplicationContext());
         mBrowserHealthReporter = new BrowserHealthReporter();
 
         if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
             if (nfc != null) {
--- a/mobile/android/base/Distribution.java
+++ b/mobile/android/base/Distribution.java
@@ -1,208 +1,363 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * ***** BEGIN LICENSE BLOCK *****
- *
  * 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/.
- *
- * ***** END LICENSE BLOCK ***** */
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONArray;
 import org.json.JSONException;
+import org.json.JSONObject;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.util.Log;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Scanner;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 public final class Distribution {
     private static final String LOGTAG = "GeckoDistribution";
 
+    private static final String DEFAULT_PREFS = GeckoApp.PREFS_NAME;
+
     private static final int STATE_UNKNOWN = 0;
     private static final int STATE_NONE = 1;
     private static final int STATE_SET = 2;
 
+    public static class DistributionDescriptor {
+        public final boolean valid;
+        public final String id;
+        public final String version;    // Example uses a float, but that's a crazy idea.
+
+        // Default UI-visible description of the distribution.
+        public final String about;
+
+        // Each distribution file can include multiple localized versions of
+        // the 'about' string. These are represented as, e.g., "about.en-US"
+        // keys in the Global object.
+        // Here we map locale to description.
+        public final Map<String, String> localizedAbout;
+
+        @SuppressWarnings("unchecked")
+        public DistributionDescriptor(JSONObject obj) {
+            this.id = obj.optString("id");
+            this.version = obj.optString("version");
+            this.about = obj.optString("about");
+            Map<String, String> loc = new HashMap<String, String>();
+            try {
+                Iterator<String> keys = obj.keys();
+                while (keys.hasNext()) {
+                    String key = keys.next();
+                    if (key.startsWith("about.")) {
+                        String locale = key.substring(6);
+                        if (!obj.isNull(locale)) {
+                            loc.put(locale, obj.getString(key));
+                        }
+                    }
+                }
+            } catch (JSONException ex) {
+                Log.w(LOGTAG, "Unable to completely process distribution JSON.", ex);
+            }
+
+            this.localizedAbout = Collections.unmodifiableMap(loc);
+            this.valid = (null != this.id) &&
+                         (null != this.version) &&
+                         (null != this.about);
+        }
+    }
+
     /**
-     * Initializes distribution if it hasn't already been initalized.
+     * Initializes distribution if it hasn't already been initalized. Sends
+     * messages to Gecko as appropriate.
      *
-     * @param packagePath specifies where to look for the distribution directory.
+     * @param packagePath where to look for the distribution directory.
      */
-    public static void init(final Context context, final String packagePath) {
+    public static void init(final Context context, final String packagePath, final String prefsPath) {
         // Read/write preferences and files on the background thread.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
-                // Bail if we've already initialized the distribution.
-                SharedPreferences settings = context.getSharedPreferences(GeckoApp.PREFS_NAME, Activity.MODE_PRIVATE);
-                String keyName = context.getPackageName() + ".distribution_state";
-                int state = settings.getInt(keyName, STATE_UNKNOWN);
-                if (state == STATE_NONE) {
-                    return;
-                }
-
-                // Send a message to Gecko if we've set a distribution.
-                if (state == STATE_SET) {
-                    GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", ""));
-                    return;
-                }
-
-                boolean distributionSet = false;
-                try {
-                    // First, try copying distribution files out of the APK.
-                    distributionSet = copyFiles(context, packagePath);
-                } catch (IOException e) {
-                    Log.e(LOGTAG, "Error copying distribution files", e);
-                }
-
-                if (!distributionSet) {
-                    // If there aren't any distribution files in the APK, look in the /system directory.
-                    File distDir = new File("/system/" + context.getPackageName() + "/distribution");
-                    if (distDir.exists()) {
-                        distributionSet = true;
-                    }
-                }
-
+                Distribution dist = new Distribution(context, packagePath, prefsPath);
+                boolean distributionSet = dist.doInit();
                 if (distributionSet) {
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", ""));
-                    settings.edit().putInt(keyName, STATE_SET).commit();
-                } else {
-                    settings.edit().putInt(keyName, STATE_NONE).commit();
                 }
             }
         });
     }
 
     /**
+     * Use <code>Context.getPackageResourcePath</code> to find an implicit
+     * package path.
+     */
+    public static void init(final Context context) {
+        Distribution.init(context, context.getPackageResourcePath(), DEFAULT_PREFS);
+    }
+
+    /**
+     * Returns parsed contents of bookmarks.json.
+     * This method should only be called from a background thread.
+     */
+    public static JSONArray getBookmarks(final Context context) {
+        Distribution dist = new Distribution(context);
+        return dist.getBookmarks();
+    }
+
+    private final Context context;
+    private final String packagePath;
+    private final String prefsBranch;
+
+    private int state = STATE_UNKNOWN;
+    private File distributionDir = null;
+
+    /**
+     * @param packagePath where to look for the distribution directory.
+     */
+    public Distribution(final Context context, final String packagePath, final String prefsBranch) {
+        this.context = context;
+        this.packagePath = packagePath;
+        this.prefsBranch = prefsBranch;
+    }
+
+    public Distribution(final Context context) {
+        this(context, context.getPackageResourcePath(), DEFAULT_PREFS);
+    }
+
+    /**
+     * Don't call from the main thread.
+     *
+     * @return true if we've set a distribution.
+     */
+    private boolean doInit() {
+        // Bail if we've already tried to initialize the distribution, and
+        // there wasn't one.
+        SharedPreferences settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE);
+        String keyName = context.getPackageName() + ".distribution_state";
+        this.state = settings.getInt(keyName, STATE_UNKNOWN);
+        if (this.state == STATE_NONE) {
+            return false;
+        }
+
+        // We've done the work once; don't do it again.
+        if (this.state == STATE_SET) {
+            // Note that we don't compute the distribution directory.
+            // Call `ensureDistributionDir` if you need it.
+            return true;
+        }
+
+        boolean distributionSet = false;
+        try {
+            // First, try copying distribution files out of the APK.
+            distributionSet = copyFiles();
+            if (distributionSet) {
+                // We always copy to the data dir, and we only copy files from
+                // a 'distribution' subdirectory. Track our dist dir now that
+                // we know it.
+                this.distributionDir = new File(getDataDir(), "distribution/");
+            }
+        } catch (IOException e) {
+            Log.e(LOGTAG, "Error copying distribution files", e);
+        }
+
+        if (!distributionSet) {
+            // If there aren't any distribution files in the APK, look in the /system directory.
+            File distDir = getSystemDistributionDir();
+            if (distDir.exists()) {
+                distributionSet = true;
+                this.distributionDir = distDir;
+            }
+        }
+
+        this.state = distributionSet ? STATE_SET : STATE_NONE;
+        settings.edit().putInt(keyName, this.state).commit();
+        return distributionSet;
+    }
+
+    /**
      * Copies the /distribution folder out of the APK and into the app's data directory.
      * Returns true if distribution files were found and copied.
      */
-    private static boolean copyFiles(Context context, String packagePath) throws IOException {
+    private boolean copyFiles() throws IOException {
         File applicationPackage = new File(packagePath);
         ZipFile zip = new ZipFile(applicationPackage);
 
         boolean distributionSet = false;
         Enumeration<? extends ZipEntry> zipEntries = zip.entries();
+
+        byte[] buffer = new byte[1024];
         while (zipEntries.hasMoreElements()) {
             ZipEntry fileEntry = zipEntries.nextElement();
             String name = fileEntry.getName();
 
-            if (!name.startsWith("distribution/"))
+            if (!name.startsWith("distribution/")) {
                 continue;
+            }
 
             distributionSet = true;
 
-            File dataDir = new File(context.getApplicationInfo().dataDir);
-            File outFile = new File(dataDir, name);
+            File outFile = new File(getDataDir(), name);
+            File dir = outFile.getParentFile();
 
-            File dir = outFile.getParentFile();
-            if (!dir.exists())
-                dir.mkdirs();
+            if (!dir.exists()) {
+                if (!dir.mkdirs()) {
+                    Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath());
+                    continue;
+                }
+            }
 
             InputStream fileStream = zip.getInputStream(fileEntry);
             OutputStream outStream = new FileOutputStream(outFile);
 
-            int b;
-            while ((b = fileStream.read()) != -1)
-                outStream.write(b);
+            int count;
+            while ((count = fileStream.read(buffer)) != -1) {
+                outStream.write(buffer, 0, count);
+            }
 
             fileStream.close();
             outStream.close();
             outFile.setLastModified(fileEntry.getTime());
         }
 
         zip.close();
 
         return distributionSet;
     }
 
     /**
-     * Returns parsed contents of bookmarks.json.
-     * This method should only be called from a background thread.
+     * After calling this method, either <code>distributionDir</code>
+     * will be set, or there is no distribution in use.
+     *
+     * Only call after init.
      */
-    public static JSONArray getBookmarks(Context context) {
-        SharedPreferences settings = context.getSharedPreferences(GeckoApp.PREFS_NAME, Activity.MODE_PRIVATE);
-        String keyName = context.getPackageName() + ".distribution_state";
-        int state = settings.getInt(keyName, STATE_UNKNOWN);
-        if (state == STATE_NONE) {
+    private File ensureDistributionDir() {
+        if (this.distributionDir != null) {
+            return this.distributionDir;
+        }
+
+        if (this.state != STATE_SET) {
             return null;
         }
 
-        ZipFile zip = null;
-        InputStream inputStream = null;
+        // After init, we know that either we've copied a distribution out of
+        // the APK, or it exists in /system/.
+        // Look in each location in turn.
+        // (This could be optimized by caching the path in shared prefs.)
+        File copied = new File(getDataDir(), "distribution/");
+        if (copied.exists()) {
+            return this.distributionDir = copied;
+        }
+        File system = getSystemDistributionDir();
+        if (system.exists()) {
+            return this.distributionDir = system;
+        }
+        return null;
+    }
+
+    /**
+     * Helper to grab a file in the distribution directory.
+     *
+     * Returns null if there is no distribution directory or the file
+     * doesn't exist. Ensures init first.
+     */
+    private File getDistributionFile(String name) {
+        Log.i(LOGTAG, "Getting file from distribution.");
+        if (this.state == STATE_UNKNOWN) {
+            if (!this.doInit()) {
+                return null;
+            }
+        }
+
+        File dist = ensureDistributionDir();
+        if (dist == null) {
+            return null;
+        }
+
+        File descFile = new File(dist, name);
+        if (!descFile.exists()) {
+            Log.e(LOGTAG, "Distribution directory exists, but no file named " + name);
+            return null;
+        }
+
+        return descFile;
+    }
+
+    public DistributionDescriptor getDescriptor() {
+        File descFile = getDistributionFile("preferences.json");
+        if (descFile == null) {
+            // Logging and existence checks are handled in getDistributionFile.
+            return null;
+        }
+
         try {
-            if (state == STATE_UNKNOWN) {
-                // If the distribution hasn't been set yet, first look for bookmarks.json in the APK.
-                File applicationPackage = new File(context.getPackageResourcePath());
-                zip = new ZipFile(applicationPackage);
-                ZipEntry zipEntry = zip.getEntry("distribution/bookmarks.json");
-                if (zipEntry != null) {
-                    inputStream = zip.getInputStream(zipEntry);
-                } else {
-                    // If there's no bookmarks.json in the APK, but there is a preferences.json,
-                    // don't create any distribution bookmarks.
-                    zipEntry = zip.getEntry("distribution/preferences.json");
-                    if (zipEntry != null) {
-                        return null;
-                    }
-                    // Otherwise, look for bookmarks.json in the /system directory.
-                    File systemFile = new File("/system/" + context.getPackageName() + "/distribution/bookmarks.json");
-                    if (!systemFile.exists()) {
-                        return null;
-                    }
-                    inputStream = new FileInputStream(systemFile);
-                }
-            } else {
-                // Otherwise, first look for the distribution in the data directory.
-                File distDir = new File(context.getApplicationInfo().dataDir, "distribution");
-                if (!distDir.exists()) {
-                    // If that doesn't exist, then we must be using a distribution from the system directory.
-                    distDir = new File("/system/" + context.getPackageName() + "/distribution");
-                }
+            JSONObject all = new JSONObject(getFileContents(descFile));
 
-                File file = new File(distDir, "bookmarks.json");
-                inputStream = new FileInputStream(file);
+            if (!all.has("Global")) {
+                Log.e(LOGTAG, "Distribution preferences.json has no Global entry!");
+                return null;
             }
 
-            // Convert input stream to JSONArray
-            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-            StringBuilder stringBuilder = new StringBuilder();
-            String s;
-            while ((s = reader.readLine()) != null) {
-                stringBuilder.append(s);
-            }
-            return new JSONArray(stringBuilder.toString());
+            return new DistributionDescriptor(all.getJSONObject("Global"));
+
+        } catch (IOException e) {
+            Log.e(LOGTAG, "Error getting distribution descriptor file.", e);
+            return null;
+        } catch (JSONException e) {
+            Log.e(LOGTAG, "Error parsing preferences.json", e);
+            return null;
+        }
+    }
+
+    public JSONArray getBookmarks() {
+        File bookmarks = getDistributionFile("bookmarks.json");
+        if (bookmarks == null) {
+            // Logging and existence checks are handled in getDistributionFile.
+            return null;
+        }
+
+        try {
+            return new JSONArray(getFileContents(bookmarks));
         } catch (IOException e) {
             Log.e(LOGTAG, "Error getting bookmarks", e);
         } catch (JSONException e) {
             Log.e(LOGTAG, "Error parsing bookmarks.json", e);
-        } finally {
-            try {
-                if (zip != null) {
-                    zip.close();
-                }
-                if (inputStream != null) {
-                    inputStream.close();
-                }
-            } catch (IOException e) {
-                Log.e(LOGTAG, "Error closing streams", e);
-            } 
         }
+
         return null;
     }
+
+    // Shortcut to slurp a file without messing around with streams.
+    private String getFileContents(File file) throws IOException {
+        Scanner scanner = null;
+        try {
+            scanner = new Scanner(file, "UTF-8");
+            return scanner.useDelimiter("\\A").next();
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+    }
+
+    private String getDataDir() {
+        return context.getApplicationInfo().dataDir;
+    }
+
+    private File getSystemDistributionDir() {
+        return new File("/system/" + context.getPackageName() + "/distribution");
+    }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -112,16 +112,17 @@ import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 abstract public class GeckoApp
                 extends GeckoActivity 
     implements GeckoEventListener, SensorEventListener, LocationListener,
@@ -1286,17 +1287,26 @@ abstract public class GeckoApp
                 editor.commit();
 
                 // The lifecycle of mHealthRecorder is "shortly after onCreate"
                 // through "onDestroy" -- essentially the same as the lifecycle
                 // of the activity itself.
                 final String profilePath = getProfile().getDir().getAbsolutePath();
                 final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
                 Log.i(LOGTAG, "Creating BrowserHealthRecorder.");
-                mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this, profilePath, dispatcher,
+                final String osLocale = Locale.getDefault().toString();
+                Log.d(LOGTAG, "Locale is " + osLocale);
+
+                // Replace the duplicate `osLocale` argument when we support switchable
+                // application locales.
+                mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this,
+                                                            profilePath,
+                                                            dispatcher,
+                                                            osLocale,
+                                                            osLocale,    // Placeholder.
                                                             previousSession);
             }
         });
 
         GeckoAppShell.setNotificationClient(makeNotificationClient());
     }
 
     protected void initializeChrome() {
@@ -1550,18 +1560,25 @@ abstract public class GeckoApp
 
                 // Kick off our background services. We do this by invoking the broadcast
                 // receiver, which uses the system alarm infrastructure to perform tasks at
                 // intervals.
                 GeckoPreferences.broadcastAnnouncementsPref(context);
                 GeckoPreferences.broadcastHealthReportUploadPref(context);
 
                 /*
-                  XXXX see bug 635342
-                   We want to disable this code if possible.  It is about 145ms in runtime
+                XXXX see Bug 635342.
+                We want to disable this code if possible.  It is about 145ms in runtime.
+
+                If this code ever becomes live again, you'll need to chain the
+                new locale into BrowserHealthRecorder correctly. See
+                GeckoAppShell.setSelectedLocale.
+                We pass the OS locale into the BHR constructor: we need to grab
+                that *before* we modify the current locale!
+
                 SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
                 String localeCode = settings.getString(getPackageName() + ".locale", "");
                 if (localeCode != null && localeCode.length() > 0)
                     GeckoAppShell.setSelectedLocale(localeCode);
                 */
 
                 if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.Launched)) {
                     return;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -1520,16 +1520,22 @@ public class GeckoAppShell
     @GeneratableAndroidBridgeTarget
     public static void setSelectedLocale(String localeCode) {
         /* Bug 713464: This method is still called from Gecko side.
            Earlier we had an option to run Firefox in a language other than system's language.
            However, this is not supported as of now.
            Gecko resets the locale to en-US by calling this function with an empty string.
            This affects GeckoPreferences activity in multi-locale builds.
 
+        N.B., if this code ever becomes live again, you need to hook it up to locale
+        recording in BrowserHealthRecorder: we track the current app and OS locales
+        as part of the recorded environment.
+
+        See similar note in GeckoApp.java for the startup path.
+
         //We're not using this, not need to save it (see bug 635342)
         SharedPreferences settings =
             getContext().getPreferences(Activity.MODE_PRIVATE);
         settings.edit().putString(getContext().getPackageName() + ".locale",
                                   localeCode).commit();
         Locale locale;
         int index;
         if ((index = localeCode.indexOf('-')) != -1 ||
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -308,34 +308,39 @@ public final class GeckoProfile {
         }
     }
 
     public String getName() {
         return mName;
     }
 
     public synchronized File getDir() {
+        forceCreate();
+        return mDir;
+    }
+
+    public synchronized GeckoProfile forceCreate() {
         if (mDir != null) {
-            return mDir;
+            return this;
         }
 
         try {
             // Check if a profile with this name already exists.
             File mozillaDir = ensureMozillaDirectory(mContext);
             mDir = findProfileDir(mozillaDir);
             if (mDir == null) {
                 // otherwise create it
                 mDir = createProfileDir(mozillaDir);
             } else {
                 Log.d(LOGTAG, "Found profile dir: " + mDir.getAbsolutePath());
             }
         } catch (IOException ioe) {
             Log.e(LOGTAG, "Error getting profile dir", ioe);
         }
-        return mDir;
+        return this;
     }
 
     public File getFile(String aFile) {
         File f = getDir();
         if (f == null)
             return null;
 
         return new File(f, aFile);
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -50,16 +50,17 @@ public class GeckoView extends LayerView
             // Set the GeckoInterface if the context is an activity and the GeckoInterface
             // has not already been set
             if (context instanceof Activity && getGeckoInterface() == null) {
                 setGeckoInterface(new BaseGeckoInterface(context));
             }
 
             Clipboard.init(context);
             HardwareUtils.init(context);
+            GeckoNetworkManager.getInstance().init(context);
 
             GeckoLoader.loadMozGlue();
             BrowserDB.setEnableContentProviders(false);
         }
 
         if (url != null) {
             GeckoThread.setUri(url);
             GeckoThread.setAction(Intent.ACTION_VIEW);
@@ -70,17 +71,17 @@ public class GeckoView extends LayerView
             Tabs tabs = Tabs.getInstance();
             tabs.attachToContext(context);
         }
         GeckoAppShell.registerEventListener("Gecko:Ready", this);
 
         ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
         initializeView(GeckoAppShell.getEventDispatcher());
 
-        GeckoProfile profile = GeckoProfile.get(context);
+        GeckoProfile profile = GeckoProfile.get(context).forceCreate();
         BrowserDB.initialize(profile.getName());
 
         if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
             GeckoAppShell.setLayerView(this);
             GeckoThread.createAndStart();
         }
     }
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -411,799 +411,22 @@ ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_
 ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png
 ICON_PATH_XHDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_96x96.png
 ICON_PATH_XXHDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_144x144.png
 else
 ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png
 ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
 endif
 
-RES_LAYOUT = \
-  $(SYNC_RES_LAYOUT) \
-  res/layout/arrow_popup.xml \
-  res/layout/autocomplete_list.xml \
-  res/layout/autocomplete_list_item.xml \
-  res/layout/bookmark_edit.xml \
-  res/layout/bookmark_folder_row.xml \
-  res/layout/bookmark_item_row.xml \
-  res/layout/browser_search.xml \
-  res/layout/browser_toolbar.xml \
-  res/layout/datetime_picker.xml \
-  res/layout/doorhanger.xml \
-  res/layout/doorhanger_button.xml \
-  res/layout/find_in_page_content.xml \
-  res/layout/font_size_preference.xml \
-  res/layout/gecko_app.xml \
-  res/layout/home_bookmarks_page.xml \
-  res/layout/home_empty_page.xml \
-  res/layout/home_empty_reading_page.xml \
-  res/layout/home_item_row.xml \
-  res/layout/home_header_row.xml \
-  res/layout/home_history_page.xml \
-  res/layout/home_history_tabs_indicator.xml \
-  res/layout/home_last_tabs_page.xml \
-  res/layout/home_history_list.xml \
-  res/layout/home_most_recent_page.xml \
-  res/layout/home_pager.xml \
-  res/layout/home_reading_list_page.xml \
-  res/layout/home_search_item_row.xml \
-  res/layout/home_banner.xml \
-  res/layout/home_suggestion_prompt.xml \
-  res/layout/home_top_sites_page.xml \
-  res/layout/icon_grid.xml \
-  res/layout/icon_grid_item.xml \
-  res/layout/web_app.xml \
-  res/layout/launch_app_list.xml \
-  res/layout/launch_app_listitem.xml \
-  res/layout/menu_action_bar.xml \
-  res/layout/menu_item_action_view.xml \
-  res/layout/menu_popup.xml \
-  res/layout/notification_icon_text.xml \
-  res/layout/notification_progress.xml \
-  res/layout/notification_progress_text.xml \
-  res/layout/pin_site_dialog.xml \
-  res/layout/preference_rightalign_icon.xml \
-  res/layout/preference_search_engine.xml \
-  res/layout/preference_search_tip.xml \
-  res/layout/site_setting_item.xml \
-  res/layout/site_setting_title.xml \
-  res/layout/shared_ui_components.xml \
-  res/layout/site_identity.xml \
-  res/layout/remote_tabs_child.xml \
-  res/layout/remote_tabs_group.xml \
-  res/layout/search_engine_row.xml \
-  res/layout/tab_menu_strip.xml \
-  res/layout/tabs_panel.xml \
-  res/layout/tabs_counter.xml \
-  res/layout/tabs_panel_header.xml \
-  res/layout/tabs_panel_indicator.xml \
-  res/layout/tabs_item_cell.xml \
-  res/layout/tabs_item_row.xml \
-  res/layout/text_selection_handles.xml \
-  res/layout/top_sites_grid_item_view.xml \
-  res/layout/two_line_page_row.xml \
-  res/layout/list_item_header.xml \
-  res/layout/select_dialog_list.xml \
-  res/layout/select_dialog_multichoice.xml \
-  res/layout/select_dialog_singlechoice.xml \
-  res/layout/simple_dropdown_item_1line.xml \
-  res/layout/suggestion_item.xml \
-  res/layout/validation_message.xml \
-  res/layout/videoplayer.xml \
-  $(NULL)
-
-RES_LAYOUT_LARGE_V11 = \
-  res/layout-large-v11/browser_toolbar.xml \
-  res/layout-large-v11/home_pager.xml \
-  $(NULL)
-
-RES_LAYOUT_LARGE_LAND_V11 = \
-  res/layout-large-land-v11/home_history_page.xml \
-  res/layout-large-land-v11/home_history_tabs_indicator.xml \
-  res/layout-large-land-v11/home_history_list.xml \
-  res/layout-large-land-v11/tabs_panel.xml \
-  res/layout-large-land-v11/tabs_panel_header.xml \
-  res/layout-large-land-v11/tabs_panel_footer.xml \
-  $(NULL)
-
-RES_LAYOUT_XLARGE_V11 = \
-  res/layout-xlarge-v11/font_size_preference.xml \
-  res/layout-xlarge-v11/home_history_page.xml \
-  res/layout-xlarge-v11/home_history_tabs_indicator.xml \
-  res/layout-xlarge-v11/home_history_list.xml \
-  res/layout-xlarge-v11/remote_tabs_child.xml \
-  res/layout-xlarge-v11/remote_tabs_group.xml \
-  $(NULL)
-
-RES_VALUES = \
-  $(SYNC_RES_VALUES) \
-  res/values/attrs.xml \
-  res/values/arrays.xml \
-  res/values/colors.xml \
-  res/values/dimens.xml \
-  res/values/integers.xml \
-  res/values/layout.xml \
-  res/values/styles.xml \
-  res/values/themes.xml \
-  $(NULL)
-
-RES_VALUES_LAND = \
-  res/values-land/integers.xml \
-  res/values-land/layout.xml \
-  res/values-land/styles.xml \
-  $(NULL)
-
-RES_VALUES_V11 = \
-  $(SYNC_RES_VALUES_V11) \
-  res/values-v11/colors.xml \
-  res/values-v11/dimens.xml \
-  res/values-v11/styles.xml \
-  res/values-v11/themes.xml \
-  $(NULL)
-
-RES_VALUES_LARGE_V11 = \
-  $(SYNC_RES_VALUES_LARGE_V11) \
-  res/values-large-v11/dimens.xml \
-  res/values-large-v11/layout.xml \
-  res/values-large-v11/styles.xml \
-  res/values-large-v11/themes.xml \
-  $(NULL)
-
-RES_VALUES_LARGE_LAND_V11 = \
-  res/values-large-land-v11/dimens.xml \
-  res/values-large-land-v11/styles.xml \
-  $(NULL)
-
-RES_VALUES_XLARGE_V11 = \
-  res/values-xlarge-v11/dimens.xml \
-  res/values-xlarge-v11/integers.xml \
-  res/values-xlarge-v11/styles.xml \
-  $(NULL)
-
-RES_VALUES_XLARGE_LAND_V11 = \
-  res/values-xlarge-land-v11/dimens.xml \
-  res/values-xlarge-land-v11/styles.xml \
-  $(NULL)
-
-RES_VALUES_V14 = \
-  res/values-v14/styles.xml \
-  $(NULL)
-
-RES_VALUES_V16 = \
-  res/values-v16/styles.xml \
-  $(NULL)
-
-RES_XML = \
-  res/xml/preferences.xml \
-  res/xml/preferences_customize.xml \
-  res/xml/preferences_display.xml \
-  res/xml/preferences_search.xml \
-  res/xml/preferences_privacy.xml \
-  res/xml/preferences_vendor.xml \
-  res/xml/preferences_devtools.xml \
-  res/xml/searchable.xml \
-  $(SYNC_RES_XML) \
-  $(NULL)
-
-RES_XML_V11 = \
-  res/xml-v11/preferences_customize.xml \
-  res/xml-v11/preference_headers.xml \
-  res/xml-v11/preferences_customize_tablet.xml \
-  res/xml-v11/preferences.xml \
-  $(NULL)
-
-RES_ANIM = \
-  res/anim/popup_show.xml \
-  res/anim/popup_hide.xml \
-  res/anim/grow_fade_in.xml \
-  res/anim/grow_fade_in_center.xml \
-  res/anim/progress_spinner.xml \
-  res/anim/shrink_fade_out.xml \
-  $(NULL)
-
-RES_DRAWABLE_MDPI = \
-  $(SYNC_RES_DRAWABLE_MDPI) \
-  res/drawable-mdpi/blank.png \
-  res/drawable-mdpi/favicon.png \
-  res/drawable-mdpi/folder.png \
-  res/drawable-mdpi/abouthome_thumbnail.png \
-  res/drawable-mdpi/alert_addon.png \
-  res/drawable-mdpi/alert_app.png \
-  res/drawable-mdpi/alert_download.png \
-  res/drawable-mdpi/alert_camera.png \
-  res/drawable-mdpi/alert_mic.png \
-  res/drawable-mdpi/alert_mic_camera.png \
-  res/drawable-mdpi/arrow_popup_bg.9.png \
-  res/drawable-mdpi/autocomplete_list_bg.9.png \
-  res/drawable-mdpi/bookmark_folder_closed.png \
-  res/drawable-mdpi/bookmark_folder_opened.png \
-  res/drawable-mdpi/desktop_notification.png \
-  res/drawable-mdpi/grid_icon_bg_activated.9.png \
-  res/drawable-mdpi/grid_icon_bg_focused.9.png \
-  res/drawable-mdpi/home_tab_menu_strip.9.png \
-  res/drawable-mdpi/ic_menu_addons_filler.png \
-  res/drawable-mdpi/ic_menu_bookmark_add.png \
-  res/drawable-mdpi/ic_menu_bookmark_remove.png \
-  res/drawable-mdpi/ic_menu_character_encoding.png \
-  res/drawable-mdpi/close.png \
-  res/drawable-mdpi/ic_menu_forward.png \
-  res/drawable-mdpi/ic_menu_guest.png \
-  res/drawable-mdpi/ic_menu_new_private_tab.png \
-  res/drawable-mdpi/ic_menu_new_tab.png \
-  res/drawable-mdpi/ic_menu_reload.png \
-  res/drawable-mdpi/ic_status_logo.png \
-  res/drawable-mdpi/ic_url_bar_go.png \
-  res/drawable-mdpi/ic_url_bar_reader.png \
-  res/drawable-mdpi/ic_url_bar_search.png \
-  res/drawable-mdpi/ic_url_bar_star.png \
-  res/drawable-mdpi/ic_url_bar_tab.png \
-  res/drawable-mdpi/icon_bookmarks_empty.png \
-  res/drawable-mdpi/icon_last_tabs.png \
-  res/drawable-mdpi/icon_last_tabs_empty.png \
-  res/drawable-mdpi/icon_most_recent.png \
-  res/drawable-mdpi/icon_most_recent_empty.png \
-  res/drawable-mdpi/icon_most_visited.png \
-  res/drawable-mdpi/icon_openinapp.png \
-  res/drawable-mdpi/icon_pageaction.png \
-  res/drawable-mdpi/icon_reading_list_empty.png \
-  res/drawable-mdpi/progress_spinner.png \
-  res/drawable-mdpi/play.png \
-  res/drawable-mdpi/pause.png \
-  res/drawable-mdpi/tab_indicator_divider.9.png \
-  res/drawable-mdpi/tab_indicator_selected.9.png \
-  res/drawable-mdpi/tab_indicator_selected_focused.9.png \
-  res/drawable-mdpi/spinner_default.9.png \
-  res/drawable-mdpi/spinner_focused.9.png \
-  res/drawable-mdpi/spinner_pressed.9.png \
-  res/drawable-mdpi/tab_new.png \
-  res/drawable-mdpi/tab_new_pb.png \
-  res/drawable-mdpi/tab_close.png \
-  res/drawable-mdpi/tab_thumbnail_default.png \
-  res/drawable-mdpi/tab_thumbnail_shadow.png \
-  res/drawable-mdpi/tabs_count.png \
-  res/drawable-mdpi/tabs_count_foreground.png \
-  res/drawable-mdpi/url_bar_entry_default.9.png \
-  res/drawable-mdpi/url_bar_entry_default_pb.9.png \
-  res/drawable-mdpi/url_bar_entry_pressed.9.png \
-  res/drawable-mdpi/url_bar_entry_pressed_pb.9.png \
-  res/drawable-mdpi/tip_addsearch.png \
-  res/drawable-mdpi/toast.9.png \
-  res/drawable-mdpi/toast_button_focused.9.png \
-  res/drawable-mdpi/toast_button_pressed.9.png \
-  res/drawable-mdpi/toast_divider.9.png \
-  res/drawable-mdpi/find_close.png \
-  res/drawable-mdpi/find_next.png \
-  res/drawable-mdpi/find_prev.png \
-  res/drawable-mdpi/larry.png \
-  res/drawable-mdpi/lock_identified.png \
-  res/drawable-mdpi/lock_verified.png \
-  res/drawable-mdpi/menu.png \
-  res/drawable-mdpi/menu_pb.png \
-  res/drawable-mdpi/menu_panel_bg.9.png \
-  res/drawable-mdpi/menu_popup_bg.9.png \
-  res/drawable-mdpi/menu_popup_arrow_bottom.png \
-  res/drawable-mdpi/menu_popup_arrow_top.png \
-  res/drawable-mdpi/menu_item_check.png \
-  res/drawable-mdpi/menu_item_more.png \
-  res/drawable-mdpi/menu_item_uncheck.png \
-  res/drawable-mdpi/pin.png \
-  res/drawable-mdpi/shield.png \
-  res/drawable-mdpi/shield_doorhanger.png \
-  res/drawable-mdpi/tabs_normal.png \
-  res/drawable-mdpi/tabs_private.png \
-  res/drawable-mdpi/tabs_synced.png \
-  res/drawable-mdpi/top_site_add.png \
-  res/drawable-mdpi/urlbar_stop.png \
-  res/drawable-mdpi/reader.png \
-  res/drawable-mdpi/reader_cropped.png \
-  res/drawable-mdpi/reader_active.png \
-  res/drawable-mdpi/reading_list.png \
-  res/drawable-mdpi/validation_arrow.png \
-  res/drawable-mdpi/validation_arrow_inverted.png \
-  res/drawable-mdpi/validation_bg.9.png \
-  res/drawable-mdpi/bookmarkdefaults_favicon_support.png \
-  res/drawable-mdpi/bookmarkdefaults_favicon_addons.png \
-  res/drawable-mdpi/handle_end.png \
-  res/drawable-mdpi/handle_middle.png \
-  res/drawable-mdpi/handle_start.png \
-  res/drawable-mdpi/scrollbar.png \
-  res/drawable-mdpi/shadow.png \
-  res/drawable-mdpi/start.png \
-  res/drawable-mdpi/marketplace.png \
-  res/drawable-mdpi/history_tabs_indicator_selected.9.png \
-  res/drawable-mdpi/warning.png \
-  res/drawable-mdpi/warning_doorhanger.png \
-  $(NULL)
-
-RES_DRAWABLE_LDPI = \
-  $(SYNC_RES_DRAWABLE_LDPI) \
-  $(NULL)
-
-RES_DRAWABLE_HDPI = \
-  $(SYNC_RES_DRAWABLE_HDPI) \
-  res/drawable-hdpi/blank.png \
-  res/drawable-hdpi/favicon.png \
-  res/drawable-hdpi/folder.png \
-  res/drawable-hdpi/home_bg.png \
-  res/drawable-hdpi/home_star.png \
-  res/drawable-hdpi/grid_icon_bg_activated.9.png \
-  res/drawable-hdpi/grid_icon_bg_focused.9.png \
-  res/drawable-hdpi/abouthome_thumbnail.png \
-  res/drawable-hdpi/alert_addon.png \
-  res/drawable-hdpi/alert_app.png \
-  res/drawable-hdpi/alert_download.png \
-  res/drawable-hdpi/bookmark_folder_closed.png \
-  res/drawable-hdpi/bookmark_folder_opened.png \
-  res/drawable-hdpi/alert_camera.png \
-  res/drawable-hdpi/alert_mic.png \
-  res/drawable-hdpi/alert_mic_camera.png \
-  res/drawable-hdpi/arrow_popup_bg.9.png \
-  res/drawable-hdpi/home_tab_menu_strip.9.png \
-  res/drawable-hdpi/ic_menu_addons_filler.png \
-  res/drawable-hdpi/ic_menu_bookmark_add.png \
-  res/drawable-hdpi/ic_menu_bookmark_remove.png \
-  res/drawable-hdpi/ic_menu_character_encoding.png \
-  res/drawable-hdpi/close.png \
-  res/drawable-hdpi/ic_menu_forward.png \
-  res/drawable-hdpi/ic_menu_guest.png \
-  res/drawable-hdpi/ic_menu_new_private_tab.png \
-  res/drawable-hdpi/ic_menu_new_tab.png \
-  res/drawable-hdpi/ic_menu_reload.png \
-  res/drawable-hdpi/ic_status_logo.png \
-  res/drawable-hdpi/ic_url_bar_go.png \
-  res/drawable-hdpi/ic_url_bar_reader.png \
-  res/drawable-hdpi/ic_url_bar_search.png \
-  res/drawable-hdpi/ic_url_bar_star.png \
-  res/drawable-hdpi/ic_url_bar_tab.png \
-  res/drawable-hdpi/icon_bookmarks_empty.png \
-  res/drawable-hdpi/icon_last_tabs.png \
-  res/drawable-hdpi/icon_last_tabs_empty.png \
-  res/drawable-hdpi/icon_most_recent.png \
-  res/drawable-hdpi/icon_most_recent_empty.png \
-  res/drawable-hdpi/icon_most_visited.png \
-  res/drawable-hdpi/icon_openinapp.png \
-  res/drawable-hdpi/icon_pageaction.png \
-  res/drawable-hdpi/icon_reading_list_empty.png \
-  res/drawable-hdpi/tab_indicator_divider.9.png \
-  res/drawable-hdpi/tab_indicator_selected.9.png \
-  res/drawable-hdpi/tab_indicator_selected_focused.9.png \
-  res/drawable-hdpi/spinner_default.9.png \
-  res/drawable-hdpi/spinner_focused.9.png \
-  res/drawable-hdpi/spinner_pressed.9.png \
-  res/drawable-hdpi/tab_new.png \
-  res/drawable-hdpi/tab_new_pb.png \
-  res/drawable-hdpi/tab_close.png \
-  res/drawable-hdpi/tab_thumbnail_default.png \
-  res/drawable-hdpi/tab_thumbnail_shadow.png \
-  res/drawable-hdpi/tabs_count.png \
-  res/drawable-hdpi/tabs_count_foreground.png \
-  res/drawable-hdpi/url_bar_entry_default.9.png \
-  res/drawable-hdpi/url_bar_entry_default_pb.9.png \
-  res/drawable-hdpi/url_bar_entry_pressed.9.png \
-  res/drawable-hdpi/url_bar_entry_pressed_pb.9.png \
-  res/drawable-hdpi/tip_addsearch.png \
-  res/drawable-hdpi/find_close.png \
-  res/drawable-hdpi/find_next.png \
-  res/drawable-hdpi/find_prev.png \
-  res/drawable-hdpi/larry.png \
-  res/drawable-hdpi/lock_identified.png \
-  res/drawable-hdpi/lock_verified.png \
-  res/drawable-hdpi/menu.png \
-  res/drawable-hdpi/menu_pb.png \
-  res/drawable-hdpi/menu_panel_bg.9.png \
-  res/drawable-hdpi/menu_popup_bg.9.png \
-  res/drawable-hdpi/menu_popup_arrow_bottom.png \
-  res/drawable-hdpi/menu_popup_arrow_top.png \
-  res/drawable-hdpi/menu_item_check.png \
-  res/drawable-hdpi/menu_item_more.png \
-  res/drawable-hdpi/menu_item_uncheck.png \
-  res/drawable-hdpi/pin.png \
-  res/drawable-hdpi/play.png \
-  res/drawable-hdpi/pause.png \
-  res/drawable-hdpi/shield.png \
-  res/drawable-hdpi/shield_doorhanger.png \
-  res/drawable-hdpi/tabs_normal.png \
-  res/drawable-hdpi/tabs_private.png \
-  res/drawable-hdpi/tabs_synced.png \
-  res/drawable-hdpi/top_site_add.png \
-  res/drawable-hdpi/urlbar_stop.png \
-  res/drawable-hdpi/reader.png \
-  res/drawable-hdpi/reader_cropped.png \
-  res/drawable-hdpi/reader_active.png \
-  res/drawable-hdpi/reading_list.png \
-  res/drawable-hdpi/validation_arrow.png \
-  res/drawable-hdpi/validation_arrow_inverted.png \
-  res/drawable-hdpi/validation_bg.9.png \
-  res/drawable-hdpi/handle_end.png \
-  res/drawable-hdpi/handle_middle.png \
-  res/drawable-hdpi/handle_start.png \
-  res/drawable-hdpi/history_tabs_indicator_selected.9.png \
-  res/drawable-hdpi/warning.png \
-  res/drawable-hdpi/warning_doorhanger.png \
-  $(NULL)
-
-RES_DRAWABLE_XHDPI = \
-  res/drawable-xhdpi/blank.png \
-  res/drawable-xhdpi/favicon.png \
-  res/drawable-xhdpi/folder.png \
-  res/drawable-xhdpi/abouthome_thumbnail.png \
-  res/drawable-xhdpi/url_bar_entry_default.9.png \
-  res/drawable-xhdpi/url_bar_entry_default_pb.9.png \
-  res/drawable-xhdpi/url_bar_entry_pressed.9.png \
-  res/drawable-xhdpi/url_bar_entry_pressed_pb.9.png \
-  res/drawable-xhdpi/alert_addon.png \
-  res/drawable-xhdpi/alert_app.png \
-  res/drawable-xhdpi/alert_download.png \
-  res/drawable-xhdpi/bookmark_folder_closed.png \
-  res/drawable-xhdpi/bookmark_folder_opened.png \
-  res/drawable-xhdpi/alert_camera.png \
-  res/drawable-xhdpi/alert_mic.png \
-  res/drawable-xhdpi/alert_mic_camera.png \
-  res/drawable-xhdpi/arrow_popup_bg.9.png \
-  res/drawable-xhdpi/home_tab_menu_strip.9.png \
-  res/drawable-xhdpi/grid_icon_bg_activated.9.png \
-  res/drawable-xhdpi/grid_icon_bg_focused.9.png \
-  res/drawable-xhdpi/ic_menu_addons_filler.png \
-  res/drawable-xhdpi/ic_menu_bookmark_add.png \
-  res/drawable-xhdpi/ic_menu_bookmark_remove.png \
-  res/drawable-xhdpi/close.png \
-  res/drawable-xhdpi/ic_menu_character_encoding.png \
-  res/drawable-xhdpi/ic_menu_forward.png \
-  res/drawable-xhdpi/ic_menu_guest.png \
-  res/drawable-xhdpi/ic_menu_new_private_tab.png \
-  res/drawable-xhdpi/ic_menu_new_tab.png \
-  res/drawable-xhdpi/ic_menu_reload.png \
-  res/drawable-xhdpi/ic_status_logo.png \
-  res/drawable-xhdpi/ic_url_bar_go.png \
-  res/drawable-xhdpi/ic_url_bar_reader.png \
-  res/drawable-xhdpi/ic_url_bar_search.png \
-  res/drawable-xhdpi/ic_url_bar_star.png \
-  res/drawable-xhdpi/ic_url_bar_tab.png \
-  res/drawable-xhdpi/icon_bookmarks_empty.png \
-  res/drawable-xhdpi/icon_last_tabs.png \
-  res/drawable-xhdpi/icon_last_tabs_empty.png \
-  res/drawable-xhdpi/icon_most_recent.png \
-  res/drawable-xhdpi/icon_most_recent_empty.png \
-  res/drawable-xhdpi/icon_most_visited.png \
-  res/drawable-xhdpi/icon_openinapp.png \
-  res/drawable-xhdpi/icon_pageaction.png \
-  res/drawable-xhdpi/icon_reading_list_empty.png \
-  res/drawable-xhdpi/spinner_default.9.png \
-  res/drawable-xhdpi/spinner_focused.9.png \
-  res/drawable-xhdpi/spinner_pressed.9.png \
-  res/drawable-xhdpi/tab_new.png \
-  res/drawable-xhdpi/tab_new_pb.png \
-  res/drawable-xhdpi/tab_close.png \
-  res/drawable-xhdpi/tab_thumbnail_default.png \
-  res/drawable-xhdpi/tab_thumbnail_shadow.png \
-  res/drawable-xhdpi/tabs_count.png \
-  res/drawable-xhdpi/tabs_count_foreground.png \
-  res/drawable-xhdpi/tip_addsearch.png \
-  res/drawable-xhdpi/find_close.png \
-  res/drawable-xhdpi/find_next.png \
-  res/drawable-xhdpi/find_prev.png \
-  res/drawable-xhdpi/top_site_add.png \
-  res/drawable-xhdpi/urlbar_stop.png \
-  res/drawable-xhdpi/reader.png \
-  res/drawable-xhdpi/reader_cropped.png \
-  res/drawable-xhdpi/reader_active.png \
-  res/drawable-xhdpi/reading_list.png \
-  res/drawable-xhdpi/larry.png \
-  res/drawable-xhdpi/lock_identified.png \
-  res/drawable-xhdpi/lock_verified.png \
-  res/drawable-xhdpi/menu.png \
-  res/drawable-xhdpi/menu_pb.png \
-  res/drawable-xhdpi/menu_panel_bg.9.png \
-  res/drawable-xhdpi/menu_popup_bg.9.png \
-  res/drawable-xhdpi/menu_popup_arrow_bottom.png \
-  res/drawable-xhdpi/menu_popup_arrow_top.png \
-  res/drawable-xhdpi/menu_item_check.png \
-  res/drawable-xhdpi/menu_item_more.png \
-  res/drawable-xhdpi/menu_item_uncheck.png \
-  res/drawable-xhdpi/pin.png \
-  res/drawable-xhdpi/play.png \
-  res/drawable-xhdpi/pause.png \
-  res/drawable-xhdpi/shield.png \
-  res/drawable-xhdpi/shield_doorhanger.png \
-  res/drawable-xhdpi/tab_indicator_divider.9.png \
-  res/drawable-xhdpi/tab_indicator_selected.9.png \
-  res/drawable-xhdpi/tab_indicator_selected_focused.9.png \
-  res/drawable-xhdpi/tabs_normal.png \
-  res/drawable-xhdpi/tabs_private.png \
-  res/drawable-xhdpi/tabs_synced.png \
-  res/drawable-xhdpi/validation_arrow.png \
-  res/drawable-xhdpi/validation_arrow_inverted.png \
-  res/drawable-xhdpi/validation_bg.9.png \
-  res/drawable-xhdpi/handle_end.png \
-  res/drawable-xhdpi/handle_middle.png \
-  res/drawable-xhdpi/handle_start.png \
-  res/drawable-xhdpi/history_tabs_indicator_selected.9.png \
-  res/drawable-xhdpi/warning.png \
-  res/drawable-xhdpi/warning_doorhanger.png \
-  $(NULL)
-
-RES_DRAWABLE_MDPI_V11 = \
-  res/drawable-mdpi-v11/alert_addon.png \
-  res/drawable-mdpi-v11/alert_app.png \
-  res/drawable-mdpi-v11/alert_download.png \
-  res/drawable-mdpi-v11/alert_camera.png \
-  res/drawable-mdpi-v11/alert_mic.png \
-  res/drawable-mdpi-v11/alert_mic_camera.png \
-  res/drawable-mdpi-v11/firefox_settings_alert.png \
-  res/drawable-mdpi-v11/ic_menu_addons.png \
-  res/drawable-mdpi-v11/ic_menu_apps.png \
-  res/drawable-mdpi-v11/ic_menu_back.png \
-  res/drawable-mdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-mdpi-v11/ic_menu_bookmark_remove.png \
-  res/drawable-mdpi-v11/ic_menu_desktop_mode_off.png \
-  res/drawable-mdpi-v11/ic_menu_desktop_mode_on.png \
-  res/drawable-mdpi-v11/ic_menu_downloads.png \
-  res/drawable-mdpi-v11/ic_menu_find_in_page.png \
-  res/drawable-mdpi-v11/ic_menu_forward.png \
-  res/drawable-mdpi-v11/ic_menu_new_private_tab.png \
-  res/drawable-mdpi-v11/ic_menu_new_tab.png \
-  res/drawable-mdpi-v11/ic_menu_reload.png \
-  res/drawable-mdpi-v11/ic_menu_save_as_pdf.png \
-  res/drawable-mdpi-v11/ic_menu_settings.png \
-  res/drawable-mdpi-v11/ic_menu_share.png \
-  res/drawable-mdpi-v11/ic_menu_tools.png \
-  res/drawable-mdpi-v11/ic_menu_quit.png \
-  res/drawable-mdpi-v11/ic_status_logo.png \
-  $(NULL)
-
-RES_DRAWABLE_HDPI_V11 = \
-  res/drawable-hdpi-v11/alert_addon.png \
-  res/drawable-hdpi-v11/alert_app.png \
-  res/drawable-hdpi-v11/alert_download.png \
-  res/drawable-hdpi-v11/alert_camera.png \
-  res/drawable-hdpi-v11/alert_mic.png \
-  res/drawable-hdpi-v11/alert_mic_camera.png \
-  res/drawable-hdpi-v11/firefox_settings_alert.png \
-  res/drawable-hdpi-v11/ic_menu_addons.png \
-  res/drawable-hdpi-v11/ic_menu_apps.png \
-  res/drawable-hdpi-v11/ic_menu_back.png \
-  res/drawable-hdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-hdpi-v11/ic_menu_bookmark_remove.png \
-  res/drawable-hdpi-v11/ic_menu_desktop_mode_off.png \
-  res/drawable-hdpi-v11/ic_menu_desktop_mode_on.png \
-  res/drawable-hdpi-v11/ic_menu_downloads.png \
-  res/drawable-hdpi-v11/ic_menu_find_in_page.png \
-  res/drawable-hdpi-v11/ic_menu_forward.png \
-  res/drawable-hdpi-v11/ic_menu_new_private_tab.png \
-  res/drawable-hdpi-v11/ic_menu_new_tab.png \
-  res/drawable-hdpi-v11/ic_menu_reload.png \
-  res/drawable-hdpi-v11/ic_menu_save_as_pdf.png \
-  res/drawable-hdpi-v11/ic_menu_settings.png \
-  res/drawable-hdpi-v11/ic_menu_share.png \
-  res/drawable-hdpi-v11/ic_menu_tools.png \
-  res/drawable-hdpi-v11/ic_menu_quit.png \
-  res/drawable-hdpi-v11/ic_status_logo.png \
-  $(NULL)
-
-RES_DRAWABLE_XHDPI_V11 = \
-  res/drawable-xhdpi-v11/alert_addon.png \
-  res/drawable-xhdpi-v11/alert_app.png \
-  res/drawable-xhdpi-v11/alert_download.png \
-  res/drawable-xhdpi-v11/alert_camera.png \
-  res/drawable-xhdpi-v11/alert_mic.png \
-  res/drawable-xhdpi-v11/alert_mic_camera.png \
-  res/drawable-xhdpi-v11/firefox_settings_alert.png \
-  res/drawable-xhdpi-v11/ic_menu_addons.png \
-  res/drawable-xhdpi-v11/ic_menu_apps.png \
-  res/drawable-xhdpi-v11/ic_menu_back.png \
-  res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-xhdpi-v11/ic_menu_bookmark_remove.png \
-  res/drawable-xhdpi-v11/ic_menu_desktop_mode_off.png \
-  res/drawable-xhdpi-v11/ic_menu_desktop_mode_on.png \
-  res/drawable-xhdpi-v11/ic_menu_downloads.png \
-  res/drawable-xhdpi-v11/ic_menu_find_in_page.png \
-  res/drawable-xhdpi-v11/ic_menu_forward.png \
-  res/drawable-xhdpi-v11/ic_menu_new_private_tab.png \
-  res/drawable-xhdpi-v11/ic_menu_new_tab.png \
-  res/drawable-xhdpi-v11/ic_menu_reload.png \
-  res/drawable-xhdpi-v11/ic_menu_save_as_pdf.png \
-  res/drawable-xhdpi-v11/ic_menu_settings.png \
-  res/drawable-xhdpi-v11/ic_menu_share.png \
-  res/drawable-xhdpi-v11/ic_menu_tools.png \
-  res/drawable-xhdpi-v11/ic_menu_quit.png \
-  res/drawable-xhdpi-v11/ic_status_logo.png \
-  $(NULL)
-
-RES_DRAWABLE_LARGE_LAND_V11 = \
-  res/drawable-large-land-v11/home_history_tabs_indicator.xml \
-  $(NULL)
-
-RES_DRAWABLE_LARGE_MDPI_V11 = \
-  res/drawable-large-mdpi-v11/arrow_popup_bg.9.png \
-  res/drawable-large-mdpi-v11/ic_menu_reload.png \
-  res/drawable-large-mdpi-v11/ic_menu_forward.png \
-  res/drawable-large-mdpi-v11/menu.png \
-  $(NULL)
-
-RES_DRAWABLE_LARGE_HDPI_V11 = \
-  res/drawable-large-hdpi-v11/arrow_popup_bg.9.png \
-  res/drawable-large-hdpi-v11/ic_menu_reload.png \
-  res/drawable-large-hdpi-v11/ic_menu_forward.png \
-  res/drawable-large-hdpi-v11/menu.png \
-  $(NULL)
-
-RES_DRAWABLE_LARGE_XHDPI_V11 = \
-  res/drawable-large-xhdpi-v11/arrow_popup_bg.9.png \
-  res/drawable-large-xhdpi-v11/ic_menu_reload.png \
-  res/drawable-large-xhdpi-v11/ic_menu_forward.png \
-  res/drawable-large-xhdpi-v11/menu.png \
-  $(NULL)
-
-RES_DRAWABLE_XLARGE_V11 = \
-  res/drawable-xlarge-v11/home_history_tabs_indicator.xml \
-  $(NULL)
-
-RES_DRAWABLE_XLARGE_MDPI_V11 = \
-  res/drawable-xlarge-mdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-xlarge-mdpi-v11/ic_menu_bookmark_remove.png \
-  $(NULL)
-
-RES_DRAWABLE_XLARGE_HDPI_V11 = \
-  res/drawable-xlarge-hdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-xlarge-hdpi-v11/ic_menu_bookmark_remove.png \
-  $(NULL)
-
-RES_DRAWABLE_XLARGE_XHDPI_V11 = \
-  res/drawable-xlarge-xhdpi-v11/ic_menu_bookmark_add.png \
-  res/drawable-xlarge-xhdpi-v11/ic_menu_bookmark_remove.png \
-  $(NULL)
-
-RES_COLOR = \
-  res/color/primary_text.xml \
-  res/color/primary_text_inverse.xml \
-  res/color/secondary_text.xml \
-  res/color/secondary_text_inverse.xml \
-  res/color/select_item_multichoice.xml \
-  res/color/tertiary_text.xml \
-  res/color/tertiary_text_inverse.xml \
-  res/color/top_sites_grid_item_title.xml \
-  res/color/url_bar_title.xml \
-  res/color/url_bar_title_hint.xml \
-  $(NULL)
-
-RES_MENU = \
-  res/menu/browser_app_menu.xml \
-  res/menu/gecko_app_menu.xml \
-  res/menu/home_contextmenu.xml \
-  res/menu/titlebar_contextmenu.xml \
-  res/menu/top_sites_contextmenu.xml \
-  res/menu-large-v11/browser_app_menu.xml \
-  res/menu-v11/browser_app_menu.xml \
-  res/menu-xlarge-v11/browser_app_menu.xml \
-  $(NULL)
-
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 ifdef MOZ_CRASHREPORTER
 FENNEC_JAVA_FILES += CrashReporter.java
-RES_DRAWABLE_MDPI += res/drawable-mdpi/crash_reporter.png
-RES_LAYOUT += res/layout/crash_reporter.xml
 endif
 
-RES_DRAWABLE += \
-  $(SYNC_RES_DRAWABLE)                                \
-  res/drawable/action_bar_button.xml                  \
-  res/drawable/action_bar_button_inverse.xml          \
-  res/drawable/top_sites_thumbnail_bg.xml             \
-  res/drawable/url_bar_bg.xml                         \
-  res/drawable/url_bar_entry.xml                      \
-  res/drawable/url_bar_nav_button.xml                 \
-  res/drawable/icon_grid_item_bg.xml                  \
-  res/drawable/url_bar_right_edge.xml                 \
-  res/drawable/bookmark_folder.xml                    \
-  res/drawable/divider_horizontal.xml                 \
-  res/drawable/divider_vertical.xml                   \
-  res/drawable/favicon_bg.xml                         \
-  res/drawable/handle_end_level.xml                   \
-  res/drawable/handle_start_level.xml                 \
-  res/drawable/home_history_tabs_indicator.xml        \
-  res/drawable/home_page_title_background.xml         \
-  res/drawable/home_banner.xml                        \
-  res/drawable/ic_menu_back.xml                       \
-  res/drawable/ic_menu_desktop_mode_off.xml           \
-  res/drawable/ic_menu_desktop_mode_on.xml            \
-  res/drawable/ic_menu_quit.xml                       \
-  res/drawable/menu_item_state.xml                    \
-  res/drawable/menu_level.xml                         \
-  res/drawable/remote_tabs_child_divider.xml          \
-  res/drawable/shaped_button.xml                      \
-  res/drawable/site_security_level.xml                \
-  res/drawable/spinner.xml                            \
-  res/drawable/suggestion_selector.xml                \
-  res/drawable/tab_new_level.xml                      \
-  res/drawable/tab_row.xml                            \
-  res/drawable/tab_thumbnail.xml                      \
-  res/drawable/tabs_panel_indicator.xml               \
-  res/drawable/textbox_bg.xml                         \
-  res/drawable/toast_button.xml                       \
-  res/drawable/webapp_titlebar_bg.xml                 \
-  $(NULL)
-
-RESOURCES = \
-  $(RES_ANIM) \
-  $(RES_COLOR) \
-  $(RES_DRAWABLE) \
-  $(RES_DRAWABLE_HDPI) \
-  $(RES_DRAWABLE_HDPI_V11) \
-  $(RES_DRAWABLE_LARGE_LAND_V11) \
-  $(RES_DRAWABLE_LARGE_HDPI_V11) \
-  $(RES_DRAWABLE_LARGE_MDPI_V11) \
-  $(RES_DRAWABLE_LARGE_XHDPI_V11) \
-  $(RES_DRAWABLE_LDPI) \
-  $(RES_DRAWABLE_MDPI) \
-  $(RES_DRAWABLE_MDPI_V11) \
-  $(RES_DRAWABLE_XHDPI) \
-  $(RES_DRAWABLE_XHDPI_V11) \
-  $(RES_DRAWABLE_XLARGE_V11) \
-  $(RES_DRAWABLE_XLARGE_HDPI_V11) \
-  $(RES_DRAWABLE_XLARGE_MDPI_V11) \
-  $(RES_DRAWABLE_XLARGE_XHDPI_V11) \
-  $(RES_LAYOUT) \
-  $(RES_LAYOUT_LARGE_LAND_V11) \
-  $(RES_LAYOUT_LARGE_V11) \
-  $(RES_LAYOUT_XLARGE_LAND_V11) \
-  $(RES_LAYOUT_XLARGE_V11) \
-  $(RES_MENU) \
-  $(RES_VALUES) \
-  $(RES_VALUES_LAND) \
-  $(RES_VALUES_LAND_V14) \
-  $(RES_VALUES_LARGE_LAND_V11) \
-  $(RES_VALUES_LARGE_V11) \
-  $(RES_VALUES_V11) \
-  $(RES_VALUES_V14) \
-  $(RES_VALUES_V16) \
-  $(RES_VALUES_XLARGE_LAND_V11) \
-  $(RES_VALUES_XLARGE_V11) \
-  $(RES_XML) \
-  $(RES_XML_V11) \
-  $(NULL)
-
-RES_DIRS= \
-  res/layout                    \
-  res/layout-large-v11          \
-  res/layout-large-land-v11     \
-  res/layout-xlarge-v11         \
-  res/values                    \
-  res/values-v11                \
-  res/values-large-v11          \
-  res/values-xlarge-land-v11    \
-  res/values-xlarge-v11         \
-  res/values-v14                \
-  res/values-v16                \
-  res/xml                       \
-  res/xml-v11                   \
-  res/anim                      \
-  res/drawable-ldpi             \
-  res/drawable-mdpi             \
-  res/drawable-hdpi             \
-  res/drawable-xhdpi            \
-  res/drawable                  \
-  res/drawable-mdpi-v11         \
-  res/drawable-hdpi-v11         \
-  res/drawable-xhdpi-v11        \
-  res/drawable-large-land-v11   \
-  res/drawable-large-mdpi-v11   \
-  res/drawable-large-hdpi-v11   \
-  res/drawable-large-xhdpi-v11  \
-  res/drawable-xlarge-v11       \
-  res/drawable-xlarge-mdpi-v11  \
-  res/drawable-xlarge-hdpi-v11  \
-  res/drawable-xlarge-xhdpi-v11 \
-  res/color                     \
-  res/menu                      \
-  res/menu-v11                  \
-  res/menu-large-v11            \
-  res/menu-xlarge-v11           \
-  $(NULL)
-
 ALL_JARS = \
   jars/gecko-browser.jar \
   jars/gecko-mozglue.jar \
   jars/gecko-util.jar \
   jars/sync-thirdparty.jar \
   jars/websockets.jar \
   $(NULL)
 
@@ -1275,16 +498,20 @@ webrtc_EXTRA_JARS := \
   jars/gecko-util.jar \
   jars/gecko-mozglue.jar \
   $(NULL)
 webrtc_JAVAC_FLAGS := -Xlint:all,-deprecation,-cast
 endif
 
 include $(topsrcdir)/config/makefiles/java-build.mk
 
+# We process ANDROID_RESFILES specially for now; the following flag
+# disables the default processing.
+IGNORE_ANDROID_RESFILES=1
+
 include $(topsrcdir)/config/rules.mk
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
 # Note that we're going to set up a dependency directly between embed_android.dex and the java files
 # Instead of on the .class files, since more than one .class file might be produced per .java file
 # Sync dependencies are provided in a single jar. Sync classes themselves are delivered as source,
@@ -1355,40 +582,38 @@ res/drawable-hdpi/icon.png: $(ICON_PATH_
 res/drawable-xhdpi/icon.png: $(ICON_PATH_XHDPI)
 	$(NSINSTALL) -D res/drawable-xhdpi
 	cp $(ICON_PATH_XHDPI) $@
 
 res/drawable-xxhdpi/icon.png: $(ICON_PATH_XXHDPI)
 	$(NSINSTALL) -D res/drawable-xxhdpi
 	cp $(ICON_PATH_XXHDPI) $@
 
-$(call mkdir_deps,$(RES_DIRS)): $(subst res/,$(srcdir)/resources/,$(RESOURCES)) Makefile
+ANDROID_RESDIRS := $(subst resources/,res/,$(sort $(dir $(ANDROID_RESFILES))))
+
+$(call mkdir_deps,$(ANDROID_RESDIRS)): $(ANDROID_RESFILES) Makefile
 	$(RM) -r $(@D)
 	$(NSINSTALL) -D $(@D)
 	$(TOUCH) $@
 
-$(RESOURCES): $(call mkdir_deps,$(RES_DIRS)) $(subst res/,$(srcdir)/resources/,$(RESOURCES))
+$(subst resources/,res/,$(ANDROID_RESFILES)): $(call mkdir_deps,$(ANDROID_RESDIRS)) $(ANDROID_RESFILES)
 	@echo "creating $@"
 	$(NSINSTALL) $(subst res/,$(srcdir)/resources/,$@) $(dir $@)
 
 res/values/strings.xml: $(call mkdir_deps,res/values)
 	$(MAKE) -C locales
 
 # With multilocale builds, there will be multiple strings.xml files. We need to
 # rebuild gecko.ap_ if any of them change.
 MULTILOCALE_STRINGS_XML_FILES := $(wildcard res/values-*/strings.xml)
 all_resources = \
-  res/drawable-mdpi/icon.png \
-  res/drawable-hdpi/icon.png \
-  res/drawable-xhdpi/icon.png \
-  res/drawable-xxhdpi/icon.png \
-  res/values/strings.xml \
   $(MULTILOCALE_STRINGS_XML_FILES) \
   AndroidManifest.xml \
-  $(RESOURCES) \
+  $(subst resources/,res/,$(ANDROID_RESFILES)) \
+  $(ANDROID_GENERATED_RESFILES) \
   $(NULL)
 
 R.java: $(all_resources)
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko --non-constant-id
 
 gecko.ap_: $(all_resources)
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
 
--- a/mobile/android/base/android-services-files.mk
+++ b/mobile/android/base/android-services-files.mk
@@ -35,16 +35,17 @@ SYNC_JAVA_FILES := \
   background/common/log/writers/StringLogWriter.java \
   background/common/log/writers/TagLogWriter.java \
   background/common/log/writers/ThreadLocalTagLogWriter.java \
   background/datareporting/TelemetryRecorder.java \
   background/db/CursorDumper.java \
   background/db/Tab.java \
   background/healthreport/Environment.java \
   background/healthreport/EnvironmentBuilder.java \
+  background/healthreport/EnvironmentV1.java \
   background/healthreport/HealthReportBroadcastReceiver.java \
   background/healthreport/HealthReportBroadcastService.java \
   background/healthreport/HealthReportDatabases.java \
   background/healthreport/HealthReportDatabaseStorage.java \
   background/healthreport/HealthReportGenerator.java \
   background/healthreport/HealthReportProvider.java \
   background/healthreport/HealthReportStorage.java \
   background/healthreport/HealthReportUtils.java \
@@ -296,63 +297,16 @@ SYNC_JAVA_FILES := \
   sync/synchronizer/UnexpectedSessionException.java \
   sync/SynchronizerConfiguration.java \
   sync/ThreadPool.java \
   sync/UnexpectedJSONException.java \
   sync/UnknownSynchronizerConfigurationVersionException.java \
   sync/Utils.java \
   $(NULL)
 
-SYNC_RES_DRAWABLE := \
-  res/drawable/pin_background.xml \
-  $(NULL)
-
-SYNC_RES_DRAWABLE_LDPI := \
-  $(NULL)
-
-SYNC_RES_DRAWABLE_MDPI := \
-  res/drawable-mdpi/desktop.png \
-  res/drawable-mdpi/mobile.png \
-  $(NULL)
-
-SYNC_RES_DRAWABLE_HDPI := \
-  $(NULL)
-
-SYNC_RES_LAYOUT := \
-  res/layout/sync_account.xml \
-  res/layout/sync_list_item.xml \
-  res/layout/sync_redirect_to_setup.xml \
-  res/layout/sync_send_tab.xml \
-  res/layout/sync_setup.xml \
-  res/layout/sync_setup_failure.xml \
-  res/layout/sync_setup_jpake_waiting.xml \
-  res/layout/sync_setup_nointernet.xml \
-  res/layout/sync_setup_pair.xml \
-  res/layout/sync_setup_success.xml \
-  res/layout/sync_setup_webview.xml \
-  $(NULL)
-
-SYNC_RES_VALUES := \
-  res/values/sync_styles.xml \
-  $(NULL)
-
-SYNC_RES_VALUES_V11 := \
-  res/values-v11/sync_styles.xml \
-  $(NULL)
-
-SYNC_RES_VALUES_LARGE_V11 := \
-  res/values-large-v11/sync_styles.xml \
-  $(NULL)
-
-SYNC_RES_XML := \
-  res/xml/sync_authenticator.xml \
-  res/xml/sync_syncadapter.xml \
-  res/xml/sync_options.xml \
-  $(NULL)
-
 SYNC_THIRDPARTY_JAVA_FILES := \
   httpclientandroidlib/androidextra/HttpClientAndroidLog.java \
   httpclientandroidlib/annotation/GuardedBy.java \
   httpclientandroidlib/annotation/Immutable.java \
   httpclientandroidlib/annotation/NotThreadSafe.java \
   httpclientandroidlib/annotation/ThreadSafe.java \
   httpclientandroidlib/auth/AUTH.java \
   httpclientandroidlib/auth/AuthenticationException.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/android-services.mozbuild
@@ -0,0 +1,28 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ANDROID_RESFILES += [
+    'resources/drawable-mdpi/desktop.png',
+    'resources/drawable-mdpi/mobile.png',
+    'resources/drawable/pin_background.xml',
+    'resources/layout/sync_account.xml',
+    'resources/layout/sync_list_item.xml',
+    'resources/layout/sync_redirect_to_setup.xml',
+    'resources/layout/sync_send_tab.xml',
+    'resources/layout/sync_setup.xml',
+    'resources/layout/sync_setup_failure.xml',
+    'resources/layout/sync_setup_jpake_waiting.xml',
+    'resources/layout/sync_setup_nointernet.xml',
+    'resources/layout/sync_setup_pair.xml',
+    'resources/layout/sync_setup_success.xml',
+    'resources/layout/sync_setup_webview.xml',
+    'resources/values-large-v11/sync_styles.xml',
+    'resources/values-v11/sync_styles.xml',
+    'resources/values/sync_styles.xml',
+    'resources/xml/sync_authenticator.xml',
+    'resources/xml/sync_options.xml',
+    'resources/xml/sync_syncadapter.xml',
+]
--- a/mobile/android/base/background/healthreport/Environment.java
+++ b/mobile/android/base/background/healthreport/Environment.java
@@ -1,274 +1,49 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.healthreport;
 
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Iterator;
-import java.util.SortedSet;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.apache.commons.codec.binary.Base64;
-import org.mozilla.gecko.background.common.log.Logger;
-
 /**
  * This captures all of the details that define an 'environment' for FHR's purposes.
  * Whenever this format changes, it'll be changing with a build ID, so no migration
  * of values is needed.
  *
  * Unless you remove the build descriptors from the set, of course.
  *
  * Or store these in a database.
  *
  * Instances of this class should be considered "effectively immutable": control their
  * scope such that clear creation/sharing boundaries exist. Once you've populated and
  * registered an <code>Environment</code>, don't do so again; start from scratch.
  *
  */
-public abstract class Environment {
-  private static final String LOG_TAG = "GeckoEnvironment";
-
-  public static int VERSION = 1;
-
-  protected final Class<? extends EnvironmentAppender> appenderClass;
-
-  protected volatile String hash = null;
-  protected volatile int id = -1;
-
-  // org.mozilla.profile.age.
-  public int profileCreation;
-
-  // org.mozilla.sysinfo.sysinfo.
-  public int cpuCount;
-  public int memoryMB;
-  public String architecture;
-  public String sysName;
-  public String sysVersion;      // Kernel.
+public abstract class Environment extends EnvironmentV1 {
+  // Version 2 adds osLocale, appLocale, acceptLangSet, and distribution.
+  public static final int CURRENT_VERSION = 2;
 
-  // geckoAppInfo. Not sure if we can/should provide this on Android.
-  public String vendor;
-  public String appName;
-  public String appID;
-  public String appVersion;
-  public String appBuildID;
-  public String platformVersion;
-  public String platformBuildID;
-  public String os;
-  public String xpcomabi;
-  public String updateChannel;
-
-  // appInfo.
-  public int isBlocklistEnabled;
-  public int isTelemetryEnabled;
-  // public int isDefaultBrowser;        // This is meaningless on Android.
-
-  // org.mozilla.addons.active.
-  public JSONObject addons = null;
-
-  // org.mozilla.addons.counts.
-  public int extensionCount;
-  public int pluginCount;
-  public int themeCount;
+  public String osLocale;                // The Android OS "Locale" value.
+  public String appLocale;
+  public int acceptLangSet;
+  public String distribution;            // ID + version. Typically empty.
 
   public Environment() {
     this(Environment.HashAppender.class);
   }
 
   public Environment(Class<? extends EnvironmentAppender> appenderClass) {
-    this.appenderClass = appenderClass;
-  }
-
-  public JSONObject getNonIgnoredAddons() {
-    if (addons == null) {
-      return null;
-    }
-    JSONObject out = new JSONObject();
-    @SuppressWarnings("unchecked")
-    Iterator<String> keys = addons.keys();
-    while (keys.hasNext()) {
-      try {
-        final String key = keys.next();
-        final Object obj = addons.get(key);
-        if (obj != null && obj instanceof JSONObject && ((JSONObject) obj).optBoolean("ignore", false)) {
-          continue;
-        }
-        out.put(key, obj);
-      } catch (JSONException ex) {
-        // Do nothing.
-      }
-    }
-    return out;
-  }
-
-  /**
-   * We break out this interface in order to allow for testing -- pass in your
-   * own appender that just records strings, for example.
-   */
-  public static abstract class EnvironmentAppender {
-    public abstract void append(String s);
-    public abstract void append(int v);
-  }
-
-  public static class HashAppender extends EnvironmentAppender {
-    final MessageDigest hasher;
-
-    public HashAppender() throws NoSuchAlgorithmException {
-      // Note to the security minded reader: we deliberately use SHA-1 here, not
-      // a stronger hash. These identifiers don't strictly need a cryptographic
-      // hash function, because there is negligible value in attacking the hash.
-      // We use SHA-1 because it's *shorter* -- the exact same reason that Git
-      // chose SHA-1.
-      hasher = MessageDigest.getInstance("SHA-1");
-    }
-
-    @Override
-    public void append(String s) {
-      try {
-        hasher.update(((s == null) ? "null" : s).getBytes("UTF-8"));
-      } catch (UnsupportedEncodingException e) {
-        // This can never occur. Thanks, Java.
-      }
-    }
-
-    @Override
-    public void append(int profileCreation) {
-      append(Integer.toString(profileCreation, 10));
-    }
-
-    @Override
-    public String toString() {
-      // We *could* use ASCII85… but the savings would be negated by the
-      // inclusion of JSON-unsafe characters like double-quote.
-      return new Base64(-1, null, false).encodeAsString(hasher.digest());
-    }
+    super(appenderClass);
+    version = CURRENT_VERSION;
   }
 
-  /**
-   * Compute the stable hash of the configured environment.
-   *
-   * @return the hash in base34, or null if there was a problem.
-   */
-  public String getHash() {
-    // It's never unset, so we only care about partial reads. volatile is enough.
-    if (hash != null) {
-      return hash;
-    }
-
-    EnvironmentAppender appender;
-    try {
-      appender = appenderClass.newInstance();
-    } catch (InstantiationException ex) {
-      // Should never happen, but...
-      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
-      return null;
-    } catch (IllegalAccessException ex) {
-      // Should never happen, but...
-      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
-      return null;
-    }
-
-    appender.append(profileCreation);
-    appender.append(cpuCount);
-    appender.append(memoryMB);
-    appender.append(architecture);
-    appender.append(sysName);
-    appender.append(sysVersion);
-    appender.append(vendor);
-    appender.append(appName);
-    appender.append(appID);
-    appender.append(appVersion);
-    appender.append(appBuildID);
-    appender.append(platformVersion);
-    appender.append(platformBuildID);
-    appender.append(os);
-    appender.append(xpcomabi);
-    appender.append(updateChannel);
-    appender.append(isBlocklistEnabled);
-    appender.append(isTelemetryEnabled);
-    appender.append(extensionCount);
-    appender.append(pluginCount);
-    appender.append(themeCount);
-
-    // We need sorted values.
-    if (addons != null) {
-      appendSortedAddons(getNonIgnoredAddons(), appender);
-    }
-
-    return hash = appender.toString();
-  }
-
-  /**
-   * Take a collection of add-on descriptors, appending a consistent string
-   * to the provided builder.
-   */
-  public static void appendSortedAddons(JSONObject addons,
-                                        final EnvironmentAppender builder) {
-    final SortedSet<String> keys = HealthReportUtils.sortedKeySet(addons);
+  @Override
+  protected void appendHash(EnvironmentAppender appender) {
+    super.appendHash(appender);
 
-    // For each add-on, produce a consistent, sorted mapping of its descriptor.
-    for (String key : keys) {
-      try {
-        JSONObject addon = addons.getJSONObject(key);
-
-        // Now produce the output for this add-on.
-        builder.append(key);
-        builder.append("={");
-
-        for (String addonKey : HealthReportUtils.sortedKeySet(addon)) {
-          builder.append(addonKey);
-          builder.append("==");
-          try {
-            builder.append(addon.get(addonKey).toString());
-          } catch (JSONException e) {
-            builder.append("_e_");
-          }
-        }
-
-        builder.append("}");
-      } catch (Exception e) {
-        // Muffle.
-        Logger.warn(LOG_TAG, "Invalid add-on for ID " + key);
-      }
-    }
-  }
-
-  public void setJSONForAddons(byte[] json) throws Exception {
-    setJSONForAddons(new String(json, "UTF-8"));
+    // v2.
+    appender.append(osLocale);
+    appender.append(appLocale);
+    appender.append(acceptLangSet);
+    appender.append(distribution);
   }
-
-  public void setJSONForAddons(String json) throws Exception {
-    if (json == null || "null".equals(json)) {
-      addons = null;
-      return;
-    }
-    addons = new JSONObject(json);
-  }
-
-  public void setJSONForAddons(JSONObject json) {
-    addons = json;
-  }
-
-  /**
-   * Includes ignored add-ons.
-   */
-  public String getNormalizedAddonsJSON() {
-    // We trust that our input will already be normalized. If that assumption
-    // is invalidated, then we'll be sorry.
-    return (addons == null) ? "null" : addons.toString();
-  }
-
-  /**
-   * Ensure that the {@link Environment} has been registered with its
-   * storage layer, and can be used to annotate events.
-   *
-   * It's safe to call this method more than once, and each time you'll
-   * get the same ID.
-   *
-   * @return the integer ID to use in subsequent DB insertions.
-   */
-  public abstract int register();
 }
--- a/mobile/android/base/background/healthreport/EnvironmentBuilder.java
+++ b/mobile/android/base/background/healthreport/EnvironmentBuilder.java
@@ -53,17 +53,23 @@ public class EnvironmentBuilder {
       Logger.error(LOG_TAG, "ContentProvider not a HealthReportProvider!", ex);
       throw ex;
     }
   }
 
   public static interface ProfileInformationProvider {
     public boolean isBlocklistEnabled();
     public boolean isTelemetryEnabled();
+    public boolean isAcceptLangUserSet();
     public long getProfileCreationTime();
+
+    public String getDistributionString();
+    public String getOSLocale();
+    public String getAppLocale();
+
     public JSONObject getAddonsJSON();
   }
 
   protected static void populateEnvironment(Environment e,
                                             ProfileInformationProvider info) {
     e.cpuCount = SysInfo.getCPUCount();
     e.memoryMB = SysInfo.getMemSize();
 
@@ -119,16 +125,22 @@ public class EnvironmentBuilder {
           Logger.debug(LOG_TAG, "Unknown add-on type: " + type);
         }
       } catch (Exception ex) {
         Logger.warn(LOG_TAG, "Failed to process add-on " + key, ex);
       }
     }
 
     e.addons = addons;
+
+    // v2 environment fields.
+    e.distribution = info.getDistributionString();
+    e.osLocale = info.getOSLocale();
+    e.appLocale = info.getAppLocale();
+    e.acceptLangSet = info.isAcceptLangUserSet() ? 1 : 0;
   }
 
   /**
    * Returns an {@link Environment} not linked to a storage instance, but
    * populated with current field values.
    *
    * @param info a source of profile data
    * @return the new {@link Environment}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/background/healthreport/EnvironmentV1.java
@@ -0,0 +1,267 @@
+/* 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/. */
+
+package org.mozilla.gecko.background.healthreport;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+import java.util.SortedSet;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.mozilla.apache.commons.codec.binary.Base64;
+import org.mozilla.gecko.background.common.log.Logger;
+
+public abstract class EnvironmentV1 {
+  private static final String LOG_TAG = "GeckoEnvironment";
+  private static final int VERSION = 1;
+
+  protected final Class<? extends EnvironmentAppender> appenderClass;
+
+  protected volatile String hash = null;
+  protected volatile int id = -1;
+
+  public int version = VERSION;
+
+  // org.mozilla.profile.age.
+  public int profileCreation;
+
+  // org.mozilla.sysinfo.sysinfo.
+  public int cpuCount;
+  public int memoryMB;
+  public String architecture;
+  public String sysName;
+  public String sysVersion;       // Kernel.
+
+  // geckoAppInfo.
+  public String vendor;
+  public String appName;
+  public String appID;
+  public String appVersion;
+  public String appBuildID;
+  public String platformVersion;
+  public String platformBuildID;
+  public String os;
+  public String xpcomabi;
+  public String updateChannel;
+
+  // appinfo.
+  public int isBlocklistEnabled;
+  public int isTelemetryEnabled;
+
+  // org.mozilla.addons.active.
+  public JSONObject addons = null;
+
+  // org.mozilla.addons.counts.
+  public int extensionCount;
+  public int pluginCount;
+  public int themeCount;
+
+  /**
+   * We break out this interface in order to allow for testing -- pass in your
+   * own appender that just records strings, for example.
+   */
+  public static abstract class EnvironmentAppender {
+    public abstract void append(String s);
+    public abstract void append(int v);
+  }
+
+  public static class HashAppender extends EnvironmentAppender {
+    final MessageDigest hasher;
+
+    public HashAppender() throws NoSuchAlgorithmException {
+      // Note to the security-minded reader: we deliberately use SHA-1 here, not
+      // a stronger hash. These identifiers don't strictly need a cryptographic
+      // hash function, because there is negligible value in attacking the hash.
+      // We use SHA-1 because it's *shorter* -- the exact same reason that Git
+      // chose SHA-1.
+      hasher = MessageDigest.getInstance("SHA-1");
+    }
+
+    @Override
+    public void append(String s) {
+      try {
+        hasher.update(((s == null) ? "null" : s).getBytes("UTF-8"));
+      } catch (UnsupportedEncodingException e) {
+        // This can never occur. Thanks, Java.
+      }
+    }
+
+    @Override
+    public void append(int profileCreation) {
+      append(Integer.toString(profileCreation, 10));
+    }
+
+    @Override
+    public String toString() {
+      // We *could* use ASCII85… but the savings would be negated by the
+      // inclusion of JSON-unsafe characters like double-quote.
+      return new Base64(-1, null, false).encodeAsString(hasher.digest());
+    }
+  }
+
+  /**
+   * Ensure that the {@link Environment} has been registered with its
+   * storage layer, and can be used to annotate events.
+   *
+   * It's safe to call this method more than once, and each time you'll
+   * get the same ID.
+   *
+   * @return the integer ID to use in subsequent DB insertions.
+   */
+  public abstract int register();
+
+  protected EnvironmentAppender getAppender() {
+    EnvironmentAppender appender = null;
+    try {
+      appender = appenderClass.newInstance();
+    } catch (InstantiationException ex) {
+      // Should never happen, but...
+      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
+    } catch (IllegalAccessException ex) {
+      // Should never happen, but...
+      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
+    }
+    return appender;
+  }
+
+  protected void appendHash(EnvironmentAppender appender) {
+    appender.append(profileCreation);
+    appender.append(cpuCount);
+    appender.append(memoryMB);
+    appender.append(architecture);
+    appender.append(sysName);
+    appender.append(sysVersion);
+    appender.append(vendor);
+    appender.append(appName);
+    appender.append(appID);
+    appender.append(appVersion);
+    appender.append(appBuildID);
+    appender.append(platformVersion);
+    appender.append(platformBuildID);
+    appender.append(os);
+    appender.append(xpcomabi);
+    appender.append(updateChannel);
+    appender.append(isBlocklistEnabled);
+    appender.append(isTelemetryEnabled);
+    appender.append(extensionCount);
+    appender.append(pluginCount);
+    appender.append(themeCount);
+
+    // We need sorted values.
+    if (addons != null) {
+      appendSortedAddons(getNonIgnoredAddons(), appender);
+    }
+  }
+
+  /**
+   * Compute the stable hash of the configured environment.
+   *
+   * @return the hash in base34, or null if there was a problem.
+   */
+  public String getHash() {
+    // It's never unset, so we only care about partial reads. volatile is enough.
+    if (hash != null) {
+      return hash;
+    }
+
+    EnvironmentAppender appender = getAppender();
+    if (appender == null) {
+      return null;
+    }
+
+    appendHash(appender);
+    return hash = appender.toString();
+  }
+
+  public EnvironmentV1(Class<? extends EnvironmentAppender> appenderClass) {
+    super();
+    this.appenderClass = appenderClass;
+  }
+
+  public JSONObject getNonIgnoredAddons() {
+    if (addons == null) {
+      return null;
+    }
+    JSONObject out = new JSONObject();
+    @SuppressWarnings("unchecked")
+    Iterator<String> keys = addons.keys();
+    while (keys.hasNext()) {
+      try {
+        final String key = keys.next();
+        final Object obj = addons.get(key);
+        if (obj != null &&
+            obj instanceof JSONObject &&
+            ((JSONObject) obj).optBoolean("ignore", false)) {
+          continue;
+        }
+        out.put(key, obj);
+      } catch (JSONException ex) {
+        // Do nothing.
+      }
+    }
+    return out;
+  }
+
+  /**
+   * Take a collection of add-on descriptors, appending a consistent string
+   * to the provided builder.
+   */
+  public static void appendSortedAddons(JSONObject addons, final EnvironmentAppender builder) {
+    final SortedSet<String> keys = HealthReportUtils.sortedKeySet(addons);
+
+    // For each add-on, produce a consistent, sorted mapping of its descriptor.
+    for (String key : keys) {
+      try {
+        JSONObject addon = addons.getJSONObject(key);
+
+        // Now produce the output for this add-on.
+        builder.append(key);
+        builder.append("={");
+
+        for (String addonKey : HealthReportUtils.sortedKeySet(addon)) {
+          builder.append(addonKey);
+          builder.append("==");
+          try {
+            builder.append(addon.get(addonKey).toString());
+          } catch (JSONException e) {
+            builder.append("_e_");
+          }
+        }
+
+        builder.append("}");
+      } catch (Exception e) {
+        // Muffle.
+        Logger.warn(LOG_TAG, "Invalid add-on for ID " + key);
+      }
+    }
+  }
+
+  public void setJSONForAddons(byte[] json) throws Exception {
+    setJSONForAddons(new String(json, "UTF-8"));
+  }
+
+  public void setJSONForAddons(String json) throws Exception {
+    if (json == null || "null".equals(json)) {
+      addons = null;
+      return;
+    }
+    addons = new JSONObject(json);
+  }
+
+  public void setJSONForAddons(JSONObject json) {
+    addons = json;
+  }
+
+  /**
+   * Includes ignored add-ons.
+   */
+  public String getNormalizedAddonsJSON() {
+    // We trust that our input will already be normalized. If that assumption
+    // is invalidated, then we'll be sorry.
+    return (addons == null) ? "null" : addons.toString();
+  }
+}
--- a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java
+++ b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java
@@ -123,26 +123,28 @@ public class HealthReportDatabaseStorage
   public static final String[] COLUMNS_HASH = new String[] {"hash"};
   public static final String[] COLUMNS_DATE_ENV_FIELD_VALUE = new String[] {"date", "env", "field", "value"};
   public static final String[] COLUMNS_DATE_ENVSTR_M_MV_F_VALUE = new String[] {
     "date", "environment", "measurement_name", "measurement_version",
     "field_name", "field_flags", "value"
   };
 
   private static final String[] COLUMNS_ENVIRONMENT_DETAILS = new String[] {
-      "id", "hash",
+      "id", "version", "hash",
       "profileCreation", "cpuCount", "memoryMB",
 
       "isBlocklistEnabled", "isTelemetryEnabled", "extensionCount",
       "pluginCount", "themeCount",
 
       "architecture", "sysName", "sysVersion", "vendor", "appName", "appID",
       "appVersion", "appBuildID", "platformVersion", "platformBuildID", "os",
       "xpcomabi", "updateChannel",
 
+      "distribution", "osLocale", "appLocale", "acceptLangSet",
+
       // Joined to the add-ons table.
       "addonsBody"
   };
 
   public static final String[] COLUMNS_MEASUREMENT_DETAILS = new String[] {"id", "name", "version"};
   public static final String[] COLUMNS_MEASUREMENT_AND_FIELD_DETAILS =
       new String[] {"measurement_name", "measurement_id", "measurement_version",
                     "field_name", "field_id", "field_flags"};
@@ -183,17 +185,17 @@ public class HealthReportDatabaseStorage
     this.fields.clear();
     this.envs.clear();
     this.measurementVersions.clear();
   }
 
   protected final HealthReportSQLiteOpenHelper helper;
 
   public static class HealthReportSQLiteOpenHelper extends SQLiteOpenHelper {
-    public static final int CURRENT_VERSION = 5;
+    public static final int CURRENT_VERSION = 6;
     public static final String LOG_TAG = "HealthReportSQL";
 
     /**
      * A little helper to avoid SQLiteOpenHelper misbehaving on Android 2.1.
      * Partly cribbed from
      * <http://stackoverflow.com/questions/5332328/sqliteopenhelper-problem-with-fully-qualified-db-path-name>.
      */
     public static class AbsolutePathContext extends ContextWrapper {
@@ -247,17 +249,20 @@ public class HealthReportDatabaseStorage
 
     @Override
     public void onCreate(SQLiteDatabase db) {
       db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                  "                     body TEXT, " +
                  "                     UNIQUE (body) " +
                  ")");
 
+      // N.B., hash collisions can occur across versions. In that case, the system
+      // is likely to persist the original environment version.
       db.execSQL("CREATE TABLE environments (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+                 "                           version INTEGER, " +
                  "                           hash TEXT, " +
                  "                           profileCreation INTEGER, " +
                  "                           cpuCount        INTEGER, " +
                  "                           memoryMB        INTEGER, " +
                  "                           isBlocklistEnabled INTEGER, " +
                  "                           isTelemetryEnabled INTEGER, " +
                  "                           extensionCount     INTEGER, " +
                  "                           pluginCount        INTEGER, " +
@@ -270,16 +275,22 @@ public class HealthReportDatabaseStorage
                  "                           appID           TEXT, " +
                  "                           appVersion      TEXT, " +
                  "                           appBuildID      TEXT, " +
                  "                           platformVersion TEXT, " +
                  "                           platformBuildID TEXT, " +
                  "                           os              TEXT, " +
                  "                           xpcomabi        TEXT, " +
                  "                           updateChannel   TEXT, " +
+
+                 "                           distribution    TEXT, " +
+                 "                           osLocale        TEXT, " +
+                 "                           appLocale       TEXT, " +
+                 "                           acceptLangSet   INTEGER, " +
+
                  "                           addonsID        INTEGER, " +
                  "                           FOREIGN KEY (addonsID) REFERENCES addons(id) ON DELETE RESTRICT, " +
                  "                           UNIQUE (hash) " +
                  ")");
 
       db.execSQL("CREATE TABLE measurements (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                  "                           name TEXT, " +
                  "                           version INTEGER, " +
@@ -352,16 +363,17 @@ public class HealthReportDatabaseStorage
       if (!db.isReadOnly()) {
         db.execSQL("PRAGMA foreign_keys=ON;");
       }
     }
 
     private void createAddonsEnvironmentsView(SQLiteDatabase db) {
       db.execSQL("CREATE VIEW environments_with_addons AS " +
           "SELECT e.id AS id, " +
+          "       e.version AS version, " +
           "       e.hash AS hash, " +
           "       e.profileCreation AS profileCreation, " +
           "       e.cpuCount AS cpuCount, " +
           "       e.memoryMB AS memoryMB, " +
           "       e.isBlocklistEnabled AS isBlocklistEnabled, " +
           "       e.isTelemetryEnabled AS isTelemetryEnabled, " +
           "       e.extensionCount AS extensionCount, " +
           "       e.pluginCount AS pluginCount, " +
@@ -374,16 +386,20 @@ public class HealthReportDatabaseStorage
           "       e.appID AS appID, " +
           "       e.appVersion AS appVersion, " +
           "       e.appBuildID AS appBuildID, " +
           "       e.platformVersion AS platformVersion, " +
           "       e.platformBuildID AS platformBuildID, " +
           "       e.os AS os, " +
           "       e.xpcomabi AS xpcomabi, " +
           "       e.updateChannel AS updateChannel, " +
+          "       e.distribution AS distribution, " +
+          "       e.osLocale AS osLocale, " +
+          "       e.appLocale AS appLocale, " +
+          "       e.acceptLangSet AS acceptLangSet, " +
           "       addons.body AS addonsBody " +
           "FROM environments AS e, addons " +
           "WHERE e.addonsID = addons.id");
     }
 
     private void upgradeDatabaseFrom2To3(SQLiteDatabase db) {
       db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                  "                     body TEXT, " +
@@ -412,31 +428,49 @@ public class HealthReportDatabaseStorage
       db.delete("fields", "measurement NOT IN (SELECT id FROM measurements)", null);
       db.delete("environments", "addonsID NOT IN (SELECT id from addons)", null);
       db.delete(EVENTS_INTEGER, "env NOT IN (SELECT id FROM environments)", null);
       db.delete(EVENTS_TEXTUAL, "env NOT IN (SELECT id FROM environments)", null);
       db.delete(EVENTS_INTEGER, "field NOT IN (SELECT id FROM fields)", null);
       db.delete(EVENTS_TEXTUAL, "field NOT IN (SELECT id FROM fields)", null);
     }
 
+    private void upgradeDatabaseFrom5to6(SQLiteDatabase db) {
+      db.execSQL("DROP VIEW environments_with_addons");
+
+      // Add version to environment (default to 1).
+      db.execSQL("ALTER TABLE environments ADD COLUMN version INTEGER DEFAULT 1");
+
+      // Add fields to environment (default to empty string).
+      db.execSQL("ALTER TABLE environments ADD COLUMN distribution TEXT DEFAULT ''");
+      db.execSQL("ALTER TABLE environments ADD COLUMN osLocale TEXT DEFAULT ''");
+      db.execSQL("ALTER TABLE environments ADD COLUMN appLocale TEXT DEFAULT ''");
+      db.execSQL("ALTER TABLE environments ADD COLUMN acceptLangSet INTEGER DEFAULT 0");
+
+      // Recreate view.
+      createAddonsEnvironmentsView(db);
+    }
+
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       if (oldVersion >= newVersion) {
         return;
       }
 
       Logger.info(LOG_TAG, "onUpgrade: from " + oldVersion + " to " + newVersion + ".");
       try {
         switch (oldVersion) {
         case 2:
           upgradeDatabaseFrom2To3(db);
         case 3:
           upgradeDatabaseFrom3To4(db);
         case 4:
           upgradeDatabaseFrom4to5(db);
+        case 5:
+          upgradeDatabaseFrom5to6(db);
         }
       } catch (Exception e) {
         Logger.error(LOG_TAG, "Failure in onUpgrade.", e);
         throw new RuntimeException(e);
       }
    }
 
     public void deleteEverything() {
@@ -531,16 +565,17 @@ public class HealthReportDatabaseStorage
       final String h = getHash();
       if (storage.envs.containsKey(h)) {
         this.id = storage.envs.get(h);
         return this.id;
       }
 
       // Otherwise, add data and hash to the DB.
       ContentValues v = new ContentValues();
+      v.put("version", version);
       v.put("hash", h);
       v.put("profileCreation", profileCreation);
       v.put("cpuCount", cpuCount);
       v.put("memoryMB", memoryMB);
       v.put("isBlocklistEnabled", isBlocklistEnabled);
       v.put("isTelemetryEnabled", isTelemetryEnabled);
       v.put("extensionCount", extensionCount);
       v.put("pluginCount", pluginCount);
@@ -553,16 +588,20 @@ public class HealthReportDatabaseStorage
       v.put("appID", appID);
       v.put("appVersion", appVersion);
       v.put("appBuildID", appBuildID);
       v.put("platformVersion", platformVersion);
       v.put("platformBuildID", platformBuildID);
       v.put("os", os);
       v.put("xpcomabi", xpcomabi);
       v.put("updateChannel", updateChannel);
+      v.put("distribution", distribution);
+      v.put("osLocale", osLocale);
+      v.put("appLocale", appLocale);
+      v.put("acceptLangSet", acceptLangSet);
 
       final SQLiteDatabase db = storage.helper.getWritableDatabase();
 
       // If we're not already, we want all of our inserts to be in a transaction.
       boolean newTransaction = !db.inTransaction();
 
       // Insert, with a little error handling to populate the cache in case of
       // omission and consequent collision.
@@ -638,16 +677,17 @@ public class HealthReportDatabaseStorage
         values.put("body", json);
         return (int) db.insert("addons", null, values);
       } finally {
         c.close();
       }
     }
 
     public void init(ContentValues v) {
+      version         = v.containsKey("version") ? v.getAsInteger("version") : Environment.CURRENT_VERSION;
       profileCreation = v.getAsInteger("profileCreation");
       cpuCount        = v.getAsInteger("cpuCount");
       memoryMB        = v.getAsInteger("memoryMB");
 
       isBlocklistEnabled = v.getAsInteger("isBlocklistEnabled");
       isTelemetryEnabled = v.getAsInteger("isTelemetryEnabled");
       extensionCount     = v.getAsInteger("extensionCount");
       pluginCount        = v.getAsInteger("pluginCount");
@@ -662,16 +702,21 @@ public class HealthReportDatabaseStorage
       appVersion      = v.getAsString("appVersion");
       appBuildID      = v.getAsString("appBuildID");
       platformVersion = v.getAsString("platformVersion");
       platformBuildID = v.getAsString("platformBuildID");
       os              = v.getAsString("os");
       xpcomabi        = v.getAsString("xpcomabi");
       updateChannel   = v.getAsString("updateChannel");
 
+      distribution    = v.getAsString("distribution");
+      osLocale        = v.getAsString("osLocale");
+      appLocale       = v.getAsString("appLocale");
+      acceptLangSet   = v.getAsInteger("acceptLangSet");
+
       try {
         setJSONForAddons(v.getAsString("addonsBody"));
       } catch (Exception e) {
         // Nothing we can do.
       }
 
       this.hash = null;
       this.id = -1;
@@ -681,16 +726,17 @@ public class HealthReportDatabaseStorage
      * Fill ourselves with data from the DB, then advance the cursor.
      *
      * @param cursor a {@link Cursor} pointing at a record to load.
      * @return true if the cursor was successfully advanced.
      */
     public boolean init(Cursor cursor) {
       int i = 0;
       this.id         = cursor.getInt(i++);
+      this.version    = cursor.getInt(i++);
       this.hash       = cursor.getString(i++);
 
       profileCreation = cursor.getInt(i++);
       cpuCount        = cursor.getInt(i++);
       memoryMB        = cursor.getInt(i++);
 
       isBlocklistEnabled = cursor.getInt(i++);
       isTelemetryEnabled = cursor.getInt(i++);
@@ -707,16 +753,21 @@ public class HealthReportDatabaseStorage
       appVersion      = cursor.getString(i++);
       appBuildID      = cursor.getString(i++);
       platformVersion = cursor.getString(i++);
       platformBuildID = cursor.getString(i++);
       os              = cursor.getString(i++);
       xpcomabi        = cursor.getString(i++);
       updateChannel   = cursor.getString(i++);
 
+      distribution    = cursor.getString(i++);
+      osLocale        = cursor.getString(i++);
+      appLocale       = cursor.getString(i++);
+      acceptLangSet   = cursor.getInt(i++);
+
       try {
         setJSONForAddons(cursor.getBlob(i++));
       } catch (Exception e) {
         // Nothing we can do.
       }
 
       return cursor.moveToNext();
     }
@@ -1334,30 +1385,32 @@ public class HealthReportDatabaseStorage
    * Deletes environments not referenced by any events except for the given current environment.
    */
   protected int deleteOrphanedEnv(final int curEnv) {
     final SQLiteDatabase db = this.helper.getWritableDatabase();
     return deleteOrphanedEnv(db, curEnv);
   }
 
   // Called internally only to ensure the same db instance is used.
+  @SuppressWarnings("static-method")
   protected int deleteOrphanedEnv(final SQLiteDatabase db, final int curEnv) {
     final String whereClause =
         "id != ? AND " +
         "id NOT IN (SELECT env FROM events)";
     final String[] whereArgs = new String[] {Integer.toString(curEnv)};
     return db.delete("environments", whereClause, whereArgs);
   }
 
   protected int deleteEventsBefore(final String dayString) {
     final SQLiteDatabase db = this.helper.getWritableDatabase();
     return deleteEventsBefore(db, dayString);
   }
 
   // Called internally only to ensure the same db instance is used.
+  @SuppressWarnings("static-method")
   protected int deleteEventsBefore(final SQLiteDatabase db, final String dayString) {
     final String whereClause = "date < ?";
     final String[] whereArgs = new String[] {dayString};
     int numEventsDeleted = 0;
     db.beginTransaction();
     try {
       numEventsDeleted += db.delete("events_integer", whereClause, whereArgs);
       numEventsDeleted += db.delete("events_textual", whereClause, whereArgs);
@@ -1372,16 +1425,17 @@ public class HealthReportDatabaseStorage
    * Deletes addons not referenced by any environments.
    */
   protected int deleteOrphanedAddons() {
     final SQLiteDatabase db = this.helper.getWritableDatabase();
     return deleteOrphanedAddons(db);
   }
 
   // Called internally only to ensure the same db instance is used.
+  @SuppressWarnings("static-method")
   protected int deleteOrphanedAddons(final SQLiteDatabase db) {
     final String whereClause = "id NOT IN (SELECT addonsID FROM environments)";
     return db.delete("addons", whereClause, null);
   }
 
   /**
    * Retrieve a mapping from a table. Keys should be unique; only one key-value
    * pair will be returned for each key.
--- a/mobile/android/base/background/healthreport/HealthReportGenerator.java
+++ b/mobile/android/base/background/healthreport/HealthReportGenerator.java
@@ -383,32 +383,125 @@ public class HealthReportGenerator {
     }
     if (current != null && changes == 0) {
       return null;
     }
     gecko.put("_v", 1);
     return gecko;
   }
 
+  // Null-safe string comparison.
+  private static boolean stringsDiffer(final String a, final String b) {
+    if (a == null) {
+      return b != null;
+    }
+    return !a.equals(b);
+  }
+
   private static JSONObject getAppInfo(Environment e, Environment current) throws JSONException {
     JSONObject appinfo = new JSONObject();
-    int changes = 0;
+
+    Logger.debug(LOG_TAG, "Generating appinfo for v" + e.version + " env " + e.hash);
+
+    // Is the environment in question newer than the diff target, or is
+    // there no diff target?
+    final boolean outdated = current == null ||
+                             e.version > current.version;
+
+    // Is the environment in question a different version (lower or higher),
+    // or is there no diff target?
+    final boolean differ = outdated || current.version > e.version;
+
+    // Always produce an output object if there's a version mismatch or this
+    // isn't a diff. Otherwise, track as we go if there's any difference.
+    boolean changed = differ;
+
+    switch (e.version) {
+    // There's a straightforward correspondence between environment versions
+    // and appinfo versions.
+    case 2:
+      appinfo.put("_v", 3);
+      break;
+    case 1:
+      appinfo.put("_v", 2);
+      break;
+    default:
+      Logger.warn(LOG_TAG, "Unknown environment version: " + e.version);
+      return appinfo;
+    }
+
+    switch (e.version) {
+    case 2:
+      if (populateAppInfoV2(appinfo, e, current, outdated)) {
+        changed = true;
+      }
+      // Fall through.
+
+    case 1:
+      // There is no older version than v1, so don't check outdated.
+      if (populateAppInfoV1(e, current, appinfo)) {
+        changed = true;
+      }
+    }
+
+    if (!changed) {
+      return null;
+    }
+
+    return appinfo;
+  }
+
+  private static boolean populateAppInfoV1(Environment e,
+                                           Environment current,
+                                           JSONObject appinfo)
+    throws JSONException {
+    boolean changes = false;
     if (current == null || current.isBlocklistEnabled != e.isBlocklistEnabled) {
       appinfo.put("isBlocklistEnabled", e.isBlocklistEnabled);
-      changes++;
+      changes = true;
     }
+
     if (current == null || current.isTelemetryEnabled != e.isTelemetryEnabled) {
       appinfo.put("isTelemetryEnabled", e.isTelemetryEnabled);
-      changes++;
+      changes = true;
+    }
+
+    return changes;
+  }
+
+  private static boolean populateAppInfoV2(JSONObject appinfo,
+                                           Environment e,
+                                           Environment current,
+                                           final boolean outdated)
+    throws JSONException {
+    boolean changes = false;
+    if (outdated ||
+        stringsDiffer(current.osLocale, e.osLocale)) {
+      appinfo.put("osLocale", e.osLocale);
+      changes = true;
     }
-    if (current != null && changes == 0) {
-      return null;
+
+    if (outdated ||
+        stringsDiffer(current.appLocale, e.appLocale)) {
+      appinfo.put("appLocale", e.appLocale);
+      changes = true;
     }
-    appinfo.put("_v", 2);
-    return appinfo;
+
+    if (outdated ||
+        stringsDiffer(current.distribution, e.distribution)) {
+      appinfo.put("distribution", e.distribution);
+      changes = true;
+    }
+
+    if (outdated ||
+        current.acceptLangSet != e.acceptLangSet) {
+      appinfo.put("acceptLangIsUserSet", e.acceptLangSet);
+      changes = true;
+    }
+    return changes;
   }
 
   private static JSONObject getAddonCounts(Environment e, Environment current) throws JSONException {
     JSONObject counts = new JSONObject();
     int changes = 0;
     if (current == null || current.extensionCount != e.extensionCount) {
       counts.put("extension", e.extensionCount);
       changes++;
--- a/mobile/android/base/background/healthreport/ProfileInformationCache.java
+++ b/mobile/android/base/background/healthreport/ProfileInformationCache.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.background.healthreport;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
+import java.util.Locale;
 import java.util.Scanner;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ProfileInformationProvider;
 
 /**
@@ -27,27 +28,50 @@ public class ProfileInformationCache imp
   private static final String LOG_TAG = "GeckoProfileInfo";
   private static final String CACHE_FILE = "profile_info_cache.json";
 
   /*
    * FORMAT_VERSION history:
    *   -: No version number; implicit v1.
    *   1: Add versioning (Bug 878670).
    *   2: Bump to regenerate add-on set after landing Bug 900694 (Bug 901622).
+   *   3: Add distribution, osLocale, appLocale.
    */
-  public static final int FORMAT_VERSION = 2;
+  public static final int FORMAT_VERSION = 3;
 
   protected boolean initialized = false;
   protected boolean needsWrite = false;
 
   protected final File file;
 
   private volatile boolean blocklistEnabled = true;
   private volatile boolean telemetryEnabled = false;
+  private volatile boolean isAcceptLangUserSet = false;
+
   private volatile long profileCreationTime = 0;
+  private volatile String distribution = "";
+
+  // There are really four kinds of locale in play:
+  //
+  // * The OS
+  // * The Android environment of the app (setDefault)
+  // * The Gecko locale
+  // * The requested content locale (Accept-Language).
+  //
+  // We track only the first two, assuming that the Gecko locale will typically
+  // be the same as the app locale.
+  //
+  // The app locale is fetched from the PIC because it can be modified at
+  // runtime -- it won't necessarily be what Locale.getDefaultLocale() returns
+  // in a fresh non-browser profile.
+  //
+  // We also track the OS locale here for the same reason -- we need to store
+  // the default (OS) value before the locale-switching code takes effect!
+  private volatile String osLocale = "";
+  private volatile String appLocale = "";
 
   private volatile JSONObject addons = null;
 
   public ProfileInformationCache(String profilePath) {
     file = new File(profilePath + File.separator + CACHE_FILE);
     Logger.pii(LOG_TAG, "Using " + file.getAbsolutePath() + " for profile information cache.");
   }
 
@@ -57,17 +81,21 @@ public class ProfileInformationCache imp
   }
 
   public JSONObject toJSON() {
     JSONObject object = new JSONObject();
     try {
       object.put("version", FORMAT_VERSION);
       object.put("blocklist", blocklistEnabled);
       object.put("telemetry", telemetryEnabled);
+      object.put("isAcceptLangUserSet", isAcceptLangUserSet);
       object.put("profileCreated", profileCreationTime);
+      object.put("osLocale", osLocale);
+      object.put("appLocale", appLocale);
+      object.put("distribution", distribution);
       object.put("addons", addons);
     } catch (JSONException e) {
       // There isn't much we can do about this.
       // Let's just quietly muffle.
       return null;
     }
     return object;
   }
@@ -81,18 +109,22 @@ public class ProfileInformationCache imp
    * @return false if there's a version mismatch or an error, true on success.
    */
   private boolean fromJSON(JSONObject object) throws JSONException {
     int version = object.optInt("version", 1);
     switch (version) {
     case FORMAT_VERSION:
       blocklistEnabled = object.getBoolean("blocklist");
       telemetryEnabled = object.getBoolean("telemetry");
+      isAcceptLangUserSet = object.getBoolean("isAcceptLangUserSet");
       profileCreationTime = object.getLong("profileCreated");
       addons = object.getJSONObject("addons");
+      distribution = object.getString("distribution");
+      osLocale = object.getString("osLocale");
+      appLocale = object.getString("appLocale");
       return true;
     default:
       Logger.warn(LOG_TAG, "Unable to restore from version " + version + " PIC file: expecting " + FORMAT_VERSION);
       return false;
     }
   }
 
   protected JSONObject readFromFile() throws FileNotFoundException, JSONException {
@@ -202,60 +234,141 @@ public class ProfileInformationCache imp
 
   public void setTelemetryEnabled(boolean value) {
     Logger.debug(LOG_TAG, "Setting telemetry enabled: " + value);
     telemetryEnabled = value;
     needsWrite = true;
   }
 
   @Override
+  public boolean isAcceptLangUserSet() {
+    ensureInitialized();
+    return isAcceptLangUserSet;
+  }
+
+  public void setAcceptLangUserSet(boolean value) {
+    Logger.debug(LOG_TAG, "Setting accept-lang as user-set: " + value);
+    isAcceptLangUserSet = value;
+    needsWrite = true;
+  }
+
+  @Override
   public long getProfileCreationTime() {
     ensureInitialized();
     return profileCreationTime;
   }
 
   public void setProfileCreationTime(long value) {
     Logger.debug(LOG_TAG, "Setting profile creation time: " + value);
     profileCreationTime = value;
     needsWrite = true;
   }
 
   @Override
+  public String getDistributionString() {
+    ensureInitialized();
+    return distribution;
+  }
+
+  /**
+   * Ensure that your arguments are non-null.
+   */
+  public void setDistributionString(String distributionID, String distributionVersion) {
+    Logger.debug(LOG_TAG, "Setting distribution: " + distributionID + ", " + distributionVersion);
+    distribution = distributionID + ":" + distributionVersion;
+    needsWrite = true;
+  }
+
+  @Override
+  public String getAppLocale() {
+    ensureInitialized();
+    return appLocale;
+  }
+
+  public void setAppLocale(String value) {
+    if (value.equalsIgnoreCase(appLocale)) {
+      return;
+    }
+    Logger.debug(LOG_TAG, "Setting app locale: " + value);
+    appLocale = value.toLowerCase(Locale.US);
+    needsWrite = true;
+  }
+
+  @Override
+  public String getOSLocale() {
+    ensureInitialized();
+    return osLocale;
+  }
+
+  public void setOSLocale(String value) {
+    if (value.equalsIgnoreCase(osLocale)) {
+      return;
+    }
+    Logger.debug(LOG_TAG, "Setting OS locale: " + value);
+    osLocale = value.toLowerCase(Locale.US);
+    needsWrite = true;
+  }
+
+  /**
+   * Update the PIC, if necessary, to match the current locale environment.
+   *
+   * @return true if the PIC needed to be updated.
+   */
+  public boolean updateLocales(String osLocale, String appLocale) {
+    if (this.osLocale.equalsIgnoreCase(osLocale) &&
+        (appLocale == null || this.appLocale.equalsIgnoreCase(appLocale))) {
+      return false;
+    }
+    this.setOSLocale(osLocale);
+    if (appLocale != null) {
+      this.setAppLocale(appLocale);
+    }
+    return true;
+  }
+
+  @Override
   public JSONObject getAddonsJSON() {
+    ensureInitialized();
     return addons;
   }
 
   public void updateJSONForAddon(String id, String json) throws Exception {
     addons.put(id, new JSONObject(json));
+    needsWrite = true;
   }
 
   public void removeAddon(String id) {
-    addons.remove(id);
+    if (null != addons.remove(id)) {
+      needsWrite = true;
+    }
   }
 
   /**
    * Will throw if you haven't done a full update at least once.
    */
   public void updateJSONForAddon(String id, JSONObject json) {
     if (addons == null) {
       throw new IllegalStateException("Cannot incrementally update add-ons without first initializing.");
     }
     try {
       addons.put(id, json);
+      needsWrite = true;
     } catch (Exception e) {
       // Why would this happen?
       Logger.warn(LOG_TAG, "Unexpected failure updating JSON for add-on.", e);
     }
   }
 
   /**
    * Update the cached set of add-ons. Throws on invalid input.
    *
    * @param json a valid add-ons JSON string.
    */
   public void setJSONForAddons(String json) throws Exception {
     addons = new JSONObject(json);
+    needsWrite = true;
   }
 
   public void setJSONForAddons(JSONObject json) {
     addons = json;
+    needsWrite = true;
   }
 }
--- a/mobile/android/base/health/BrowserHealthRecorder.java
+++ b/mobile/android/base/health/BrowserHealthRecorder.java
@@ -8,21 +8,21 @@ package org.mozilla.gecko.health;
 import java.util.ArrayList;
 
 import android.content.Context;
 import android.content.ContentProviderClient;
 import android.content.SharedPreferences;
 import android.util.Log;
 
 import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.Distribution;
+import org.mozilla.gecko.Distribution.DistributionDescriptor;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
-import org.mozilla.gecko.PrefsHelper;
-import org.mozilla.gecko.PrefsHelper.PrefHandler;
 
 import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
 import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
 import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
 import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
 import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields.FieldSpec;
 import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
 
@@ -33,40 +33,41 @@ import org.mozilla.gecko.util.ThreadUtil
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.Scanner;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * BrowserHealthRecorder is the browser's interface to the Firefox Health
  * Report storage system. It manages environments (a collection of attributes
  * that are tracked longitudinally) on the browser's behalf, exposing a simpler
  * interface for recording changes.
  *
  * Keep an instance of this class around.
  *
  * Tell it when an environment attribute has changed: call {@link
- * #onBlocklistPrefChanged(boolean)} or {@link
- * #onTelemetryPrefChanged(boolean)}, followed by {@link
+ * #onAppLocaleChanged(String)} followed by {@link
  * #onEnvironmentChanged()}.
  *
  * Use it to record events: {@link #recordSearch(String, String)}.
  *
  * Shut it down when you're done being a browser: {@link #close()}.
  */
 public class BrowserHealthRecorder implements GeckoEventListener {
     private static final String LOG_TAG = "GeckoHealthRec";
+    private static final String PREF_ACCEPT_LANG = "intl.accept_languages";
     private static final String PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
-    private static final String EVENT_ADDONS_ALL = "Addons:All";
+    private static final String EVENT_SNAPSHOT = "HealthReport:Snapshot";
     private static final String EVENT_ADDONS_CHANGE = "Addons:Change";
     private static final String EVENT_ADDONS_UNINSTALLING = "Addons:Uninstalling";
     private static final String EVENT_PREF_CHANGE = "Pref:Change";
 
     // This is raised from Gecko and signifies a search via the URL bar (not a bookmarks keyword
     // search). Using this event (rather than passing the invocation location as an arg) avoids
     // browser.js having to know about the invocation location.
     public static final String EVENT_KEYWORD_SEARCH = "Search:Keyword";
@@ -237,18 +238,25 @@ public class BrowserHealthRecorder imple
      * This changes in certain circumstances; be sure to use the current value when recording data.
      */
     private void setHealthEnvironment(final int env) {
         this.env = env;
     }
 
     /**
      * This constructor does IO. Run it on a background thread.
+     *
+     * appLocale can be null, which indicates that it will be provided later.
      */
-    public BrowserHealthRecorder(final Context context, final String profilePath, final EventDispatcher dispatcher, SessionInformation previousSession) {
+    public BrowserHealthRecorder(final Context context,
+                                 final String profilePath,
+                                 final EventDispatcher dispatcher,
+                                 final String osLocale,
+                                 final String appLocale,
+                                 SessionInformation previousSession) {
         Log.d(LOG_TAG, "Initializing. Dispatcher is " + dispatcher);
         this.dispatcher = dispatcher;
         this.previousSession = previousSession;
 
         this.client = EnvironmentBuilder.getContentProviderClient(context);
         if (this.client == null) {
             throw new IllegalStateException("Could not fetch Health Report content provider.");
         }
@@ -258,19 +266,22 @@ public class BrowserHealthRecorder imple
             // Stick around even if we don't have storage: eventually we'll
             // want to report total failures of FHR storage itself, and this
             // way callers don't need to worry about whether their health
             // recorder didn't initialize.
             this.client.release();
             this.client = null;
         }
 
+        // Note that the PIC is not necessarily fully initialized at this point:
+        // we haven't set the app locale. This must be done before an environment
+        // is recorded.
         this.profileCache = new ProfileInformationCache(profilePath);
         try {
-            this.initialize(context, profilePath);
+            this.initialize(context, profilePath, osLocale, appLocale);
         } catch (Exception e) {
             Log.e(LOG_TAG, "Exception initializing.", e);
         }
     }
 
     /**
      * Shut down database connections, unregister event listeners, and perform
      * provider-specific uninitialization.
@@ -294,32 +305,27 @@ public class BrowserHealthRecorder imple
         this.storage = null;
         if (this.client != null) {
             this.client.release();
             this.client = null;
         }
     }
 
     private void unregisterEventListeners() {
-        this.dispatcher.unregisterEventListener(EVENT_ADDONS_ALL, this);
+        this.dispatcher.unregisterEventListener(EVENT_SNAPSHOT, this);
         this.dispatcher.unregisterEventListener(EVENT_ADDONS_CHANGE, this);
         this.dispatcher.unregisterEventListener(EVENT_ADDONS_UNINSTALLING, this);
         this.dispatcher.unregisterEventListener(EVENT_PREF_CHANGE, this);
         this.dispatcher.unregisterEventListener(EVENT_KEYWORD_SEARCH, this);
         this.dispatcher.unregisterEventListener(EVENT_SEARCH, this);
     }
 
-    public void onBlocklistPrefChanged(boolean to) {
+    public void onAppLocaleChanged(String to) {
         this.profileCache.beginInitialization();
-        this.profileCache.setBlocklistEnabled(to);
-    }
-
-    public void onTelemetryPrefChanged(boolean to) {
-        this.profileCache.beginInitialization();
-        this.profileCache.setTelemetryEnabled(to);
+        this.profileCache.setAppLocale(to);
     }
 
     public void onAddonChanged(String id, JSONObject json) {
         this.profileCache.beginInitialization();
         try {
             this.profileCache.updateJSONForAddon(id, json);
         } catch (IllegalStateException e) {
             Log.w(LOG_TAG, "Attempted to update add-on cache prior to full init.", e);
@@ -335,18 +341,17 @@ public class BrowserHealthRecorder imple
         }
     }
 
     /**
      * Call this when a material change might have occurred in the running
      * environment, such that a new environment should be computed and prepared
      * for use in future events.
      *
-     * Invoke this method after calls that mutate the environment, such as
-     * {@link #onBlocklistPrefChanged(boolean)}.
+     * Invoke this method after calls that mutate the environment.
      *
      * If this change resulted in a transition between two environments, {@link
      * #onEnvironmentTransition(int, int)} will be invoked on the background
      * thread.
      */
     public synchronized void onEnvironmentChanged() {
         final int previousEnv = this.env;
         this.env = -1;
@@ -486,24 +491,46 @@ public class BrowserHealthRecorder imple
                 Log.w(LOG_TAG, "Couldn't write times.json.", e);
             }
         }
 
         Log.d(LOG_TAG, "Incorporating environment: profile creation = " + time);
         return time;
     }
 
-    private void handlePrefValue(final String pref, final boolean value) {
-        Log.d(LOG_TAG, "Incorporating environment: " + pref + " = " + value);
-        if (AppConstants.TELEMETRY_PREF_NAME.equals(pref)) {
-            profileCache.setTelemetryEnabled(value);
+    private void onPrefMessage(final String pref, final JSONObject message) {
+        Log.d(LOG_TAG, "Incorporating environment: " + pref);
+        if (PREF_ACCEPT_LANG.equals(pref)) {
+            // We only record whether this is user-set.
+            try {
+                this.profileCache.beginInitialization();
+                this.profileCache.setAcceptLangUserSet(message.getBoolean("isUserSet"));
+            } catch (JSONException ex) {
+                Log.w(LOG_TAG, "Unexpected JSONException fetching isUserSet for " + pref);
+            }
             return;
         }
-        if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
-            profileCache.setBlocklistEnabled(value);
+
+        // (We only handle boolean prefs right now.)
+        try {
+            boolean value = message.getBoolean("value");
+
+            if (AppConstants.TELEMETRY_PREF_NAME.equals(pref)) {
+                this.profileCache.beginInitialization();
+                this.profileCache.setTelemetryEnabled(value);
+                return;
+            }
+
+            if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
+                this.profileCache.beginInitialization();
+                this.profileCache.setBlocklistEnabled(value);
+                return;
+            }
+        } catch (JSONException ex) {
+            Log.w(LOG_TAG, "Unexpected JSONException fetching boolean value for " + pref);
             return;
         }
         Log.w(LOG_TAG, "Unexpected pref: " + pref);
     }
 
     /**
      * Background init helper.
      */
@@ -566,57 +593,55 @@ public class BrowserHealthRecorder imple
             }
         });
     }
 
     /**
      * Add provider-specific initialization in this method.
      */
     private synchronized void initialize(final Context context,
-                                         final String profilePath)
+                                         final String profilePath,
+                                         final String osLocale,
+                                         final String appLocale)
         throws java.io.IOException {
 
         Log.d(LOG_TAG, "Initializing profile cache.");
         this.state = State.INITIALIZING;
 
         // If we can restore state from last time, great.
         if (this.profileCache.restoreUnlessInitialized()) {
+            this.profileCache.updateLocales(osLocale, appLocale);
+            this.profileCache.completeInitialization();
+
             Log.d(LOG_TAG, "Successfully restored state. Initializing storage.");
             initializeStorage();
             return;
         }
 
         // Otherwise, let's initialize it from scratch.
         this.profileCache.beginInitialization();
         this.profileCache.setProfileCreationTime(getAndPersistProfileInitTime(context, profilePath));
-
-        final BrowserHealthRecorder self = this;
+        this.profileCache.setOSLocale(osLocale);
+        this.profileCache.setAppLocale(appLocale);
 
-        PrefHandler handler = new PrefsHelper.PrefHandlerBase() {
-            @Override
-            public void prefValue(String pref, boolean value) {
-                handlePrefValue(pref, value);
-            }
-
+        // Because the distribution lookup can take some time, do it at the end of
+        // our background startup work, along with the Gecko snapshot fetch.
+        final GeckoEventListener self = this;
+        ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
-            public void finish() {
-                Log.d(LOG_TAG, "Requesting all add-ons from Gecko.");
-                dispatcher.registerEventListener(EVENT_ADDONS_ALL, self);
-                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Addons:FetchAll", null));
-                // Wait for the broadcast event which completes our initialization.
+            public void run() {
+                final DistributionDescriptor desc = new Distribution(context).getDescriptor();
+                if (desc != null && desc.valid) {
+                    profileCache.setDistributionString(desc.id, desc.version);
+                }
+                Log.d(LOG_TAG, "Requesting all add-ons and FHR prefs from Gecko.");
+                dispatcher.registerEventListener(EVENT_SNAPSHOT, self);
+                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HealthReport:RequestSnapshot", null));
             }
-        };
-
-        // Oh, singletons.
-        PrefsHelper.getPrefs(new String[] {
-                                 AppConstants.TELEMETRY_PREF_NAME,
-                                 PREF_BLOCKLIST_ENABLED
-                             },
-                             handler);
-        Log.d(LOG_TAG, "Requested prefs.");
+        });
     }
 
     /**
      * Invoked in the background whenever the environment transitions between
      * two valid values.
      */
     protected void onEnvironmentTransition(int prev, int env) {
         if (this.state != State.INITIALIZED) {
@@ -633,22 +658,32 @@ public class BrowserHealthRecorder imple
         setCurrentSession(newSession);
         newSession.recordBegin(editor);
         editor.commit();
     }
 
     @Override
     public void handleMessage(String event, JSONObject message) {
         try {
-            if (EVENT_ADDONS_ALL.equals(event)) {
-                Log.d(LOG_TAG, "Got all add-ons.");
+            if (EVENT_SNAPSHOT.equals(event)) {
+                Log.d(LOG_TAG, "Got all add-ons and prefs.");
                 try {
-                    JSONObject addons = message.getJSONObject("json");
+                    JSONObject json = message.getJSONObject("json");
+                    JSONObject addons = json.getJSONObject("addons");
                     Log.i(LOG_TAG, "Persisting " + addons.length() + " add-ons.");
                     profileCache.setJSONForAddons(addons);
+
+                    JSONObject prefs = json.getJSONObject("prefs");
+                    Log.i(LOG_TAG, "Persisting prefs.");
+                    Iterator<?> keys = prefs.keys();
+                    while (keys.hasNext()) {
+                        String pref = (String) keys.next();
+                        this.onPrefMessage(pref, prefs.getJSONObject(pref));
+                    }
+
                     profileCache.completeInitialization();
                 } catch (java.io.IOException e) {
                     Log.e(LOG_TAG, "Error completing profile cache initialization.", e);
                     state = State.INITIALIZATION_FAILED;
                     return;
                 }
 
                 if (state == State.INITIALIZING) {
@@ -670,17 +705,17 @@ public class BrowserHealthRecorder imple
                 this.onAddonChanged(message.getString("id"), message.getJSONObject("json"));
                 this.onEnvironmentChanged();
                 return;
             }
 
             if (EVENT_PREF_CHANGE.equals(event))