Bug 1234522 - Remove services/datareporting. r=gfritzsche
☠☠ backed out by 19a2342819e4 ☠ ☠
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Tue, 05 Jan 2016 02:01:00 +0100
changeset 320134 3e7dc6d87325ce8f6d817e7f0bad096152cc8aef
parent 320133 f6447d37d113b86e2bf72356da1cb67dcd49ad97
child 320135 c3b6bf176f86b0167f008fbf87e5a2aa39061584
push id9143
push userahunt@mozilla.com
push dateFri, 08 Jan 2016 21:30:53 +0000
reviewersgfritzsche
bugs1234522
milestone46.0a1
Bug 1234522 - Remove services/datareporting. r=gfritzsche
browser/base/content/browser-data-submission-info-bar.js
browser/installer/package-manifest.in
modules/libpref/greprefs.js
services/datareporting/DataReporting.manifest
services/datareporting/DataReportingService.js
services/datareporting/datareporting-prefs.js
services/datareporting/modules-testing/mocks.jsm
services/datareporting/moz.build
services/datareporting/policy.jsm
services/datareporting/tests/xpcshell/head.js
services/datareporting/tests/xpcshell/test_policy.js
services/datareporting/tests/xpcshell/xpcshell.ini
services/docs/datareporting.rst
services/docs/index.rst
services/moz.build
toolkit/components/telemetry/datareporting-prefs.js
toolkit/modules/SessionRecorder.jsm
--- a/browser/base/content/browser-data-submission-info-bar.js
+++ b/browser/base/content/browser-data-submission-info-bar.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+const LOGGER_NAME = "Toolkit.Telemetry";
+const LOGGER_PREFIX = "DataNotificationInfoBar::";
+
 /**
  * Represents an info bar that shows a data submission notification.
  */
 var gDataNotificationInfoBar = {
   _OBSERVERS: [
     "datareporting:notify-data-policy:request",
     "datareporting:notify-data-policy:close",
   ],
@@ -16,17 +19,17 @@ var gDataNotificationInfoBar = {
   get _notificationBox() {
     delete this._notificationBox;
     return this._notificationBox = document.getElementById("global-notificationbox");
   },
 
   get _log() {
     let Log = Cu.import("resource://gre/modules/Log.jsm", {}).Log;
     delete this._log;
-    return this._log = Log.repository.getLogger("Services.DataReporting.InfoBar");
+    return this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
   },
 
   init: function() {
     window.addEventListener("unload", () => {
       for (let o of this._OBSERVERS) {
         Services.obs.removeObserver(this, o);
       }
     }, false);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -496,20 +496,16 @@
 #endif
 #ifdef XP_MACOSX
 @RESPATH@/browser/components/SafariProfileMigrator.js
 #endif
 @RESPATH@/components/nsINIProcessor.manifest
 @RESPATH@/components/nsINIProcessor.js
 @RESPATH@/components/nsPrompter.manifest
 @RESPATH@/components/nsPrompter.js
-#ifdef MOZ_DATA_REPORTING
-@RESPATH@/components/DataReporting.manifest
-@RESPATH@/components/DataReportingService.js
-#endif
 #ifdef MOZ_SERVICES_HEALTHREPORT
 @RESPATH@/components/HealthReportComponents.manifest
 @RESPATH@/browser/components/SelfSupportService.manifest
 @RESPATH@/browser/components/SelfSupportService.js
 #endif
 @RESPATH@/components/SyncComponents.manifest
 @RESPATH@/components/Weave.js
 @RESPATH@/components/CaptivePortalDetectComponents.manifest
--- a/modules/libpref/greprefs.js
+++ b/modules/libpref/greprefs.js
@@ -1,12 +1,12 @@
 #include ../../netwerk/base/security-prefs.js
 #include init/all.js
 #ifdef MOZ_DATA_REPORTING
-#include ../../services/datareporting/datareporting-prefs.js
+#include ../../toolkit/components/telemetry/datareporting-prefs.js
 #endif
 #ifdef MOZ_SERVICES_HEALTHREPORT
 #if MOZ_WIDGET_TOOLKIT == android
 #include ../../mobile/android/chrome/content/healthreport-prefs.js
 #else
 #include ../../services/healthreport/healthreport-prefs.js
 #endif
 #endif
deleted file mode 100644
--- a/services/datareporting/DataReporting.manifest
+++ /dev/null
@@ -1,16 +0,0 @@
-#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
-#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
-#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
-#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
-#   suite (comm):   {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}
-#   graphene:       {d1bfe7d9-c01e-4237-998b-7b5f960a4314}
-
-# The Data Reporting Service drives collection and submission of metrics
-# and other useful data to Mozilla. It drives the display of the data
-# submission notification info bar and thus is required by Firefox Health
-# Report and Telemetry.
-
-component {41f6ae36-a79f-4613-9ac3-915e70f83789} DataReportingService.js
-contract @mozilla.org/datareporting/service;1 {41f6ae36-a79f-4613-9ac3-915e70f83789}
-category app-startup DataReportingService service,@mozilla.org/datareporting/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314}
-
deleted file mode 100644
--- a/services/datareporting/DataReportingService.js
+++ /dev/null
@@ -1,296 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/ClientID.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Log",
-                                  "resource://gre/modules/Log.jsm");
-
-const ROOT_BRANCH = "datareporting.";
-const POLICY_BRANCH = ROOT_BRANCH + "policy.";
-const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport.";
-const HEALTHREPORT_LOGGING_BRANCH = HEALTHREPORT_BRANCH + "logging.";
-const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000;
-const DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC = 60 * 1000;
-
-/**
- * The Firefox Health Report XPCOM service.
- *
- * External consumers will be interested in the "reporter" property of this
- * service. This property is a `HealthReporter` instance that powers the
- * service. The property may be null if the Health Report service is not
- * enabled.
- *
- * EXAMPLE USAGE
- * =============
- *
- * let reporter = Cc["@mozilla.org/datareporting/service;1"]
- *                  .getService(Ci.nsISupports)
- *                  .wrappedJSObject
- *                  .healthReporter;
- *
- * if (reporter.haveRemoteData) {
- *   // ...
- * }
- *
- * IMPLEMENTATION NOTES
- * ====================
- *
- * In order to not adversely impact application start time, the `HealthReporter`
- * instance is not initialized until a few seconds after "final-ui-startup."
- * The exact delay is configurable via preferences so it can be adjusted with
- * a hotfix extension if the default value is ever problematic. Because of the
- * overhead with the initial creation of the database, the first run is delayed
- * even more than subsequent runs. This does mean that the first moments of
- * browser activity may be lost by FHR.
- *
- * Shutdown of the `HealthReporter` instance is handled completely within the
- * instance (it registers observers on initialization). See the notes on that
- * type for more.
- */
-this.DataReportingService = function () {
-  this.wrappedJSObject = this;
-
-  this._quitting = false;
-
-  this._os = Cc["@mozilla.org/observer-service;1"]
-               .getService(Ci.nsIObserverService);
-}
-
-DataReportingService.prototype = Object.freeze({
-  classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
-
-  //---------------------------------------------
-  // Start of policy listeners.
-  //---------------------------------------------
-
-  /**
-   * Called when policy requests data upload.
-   */
-  onRequestDataUpload: function (request) {
-    if (!this.healthReporter) {
-      return;
-    }
-
-    this.healthReporter.requestDataUpload(request);
-  },
-
-  onNotifyDataPolicy: function (request) {
-    Observers.notify("datareporting:notify-data-policy:request", request);
-  },
-
-  onRequestRemoteDelete: function (request) {
-    if (!this.healthReporter) {
-      return;
-    }
-
-    this.healthReporter.deleteRemoteData(request);
-  },
-
-  //---------------------------------------------
-  // End of policy listeners.
-  //---------------------------------------------
-
-  observe: function observe(subject, topic, data) {
-    switch (topic) {
-      case "app-startup":
-        this._os.addObserver(this, "profile-after-change", true);
-        break;
-
-      case "profile-after-change":
-        this._os.removeObserver(this, "profile-after-change");
-
-        try {
-          this._prefs = new Preferences(HEALTHREPORT_BRANCH);
-
-          // We can't interact with prefs until after the profile is present.
-          let policyPrefs = new Preferences(POLICY_BRANCH);
-          this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this);
-
-          this._os.addObserver(this, "sessionstore-windows-restored", true);
-        } catch (ex) {
-          Cu.reportError("Exception when initializing data reporting service: " +
-                         Log.exceptionStr(ex));
-        }
-        break;
-
-      case "sessionstore-windows-restored":
-        this._os.removeObserver(this, "sessionstore-windows-restored");
-        this._os.addObserver(this, "quit-application", false);
-
-        let policy = this.policy;
-        policy.startPolling();
-
-        // Don't initialize Firefox Health Reporter collection and submission
-        // service unless it is enabled.
-        if (!this._prefs.get("service.enabled", true)) {
-          return;
-        }
-
-        let haveFirstRun = this._prefs.get("service.firstRun", false);
-        let delayInterval;
-
-        if (haveFirstRun) {
-          delayInterval = this._prefs.get("service.loadDelayMsec") ||
-                          DEFAULT_LOAD_DELAY_MSEC;
-        } else {
-          delayInterval = this._prefs.get("service.loadDelayFirstRunMsec") ||
-                          DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC;
-        }
-
-        // Delay service loading a little more so things have an opportunity
-        // to cool down first.
-        this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        this.timer.initWithCallback({
-          notify: function notify() {
-            delete this.timer;
-
-            // There could be a race between "quit-application" firing and
-            // this callback being invoked. We close that door.
-            if (this._quitting) {
-              return;
-            }
-
-            // Side effect: instantiates the reporter instance if not already
-            // accessed.
-            //
-            // The instance installs its own shutdown observers. So, we just
-            // fire and forget: it will clean itself up.
-            let reporter = this.healthReporter;
-            policy.ensureUserNotified();
-          }.bind(this),
-        }, delayInterval, this.timer.TYPE_ONE_SHOT);
-
-        break;
-
-      case "quit-application":
-        this._os.removeObserver(this, "quit-application");
-        this._quitting = true;
-
-        // Shutdown doesn't clear pending timers. So, we need to explicitly
-        // cancel our health reporter initialization timer or else it will
-        // attempt initialization after shutdown has commenced. This would
-        // likely lead to stalls or crashes.
-        if (this.timer) {
-          this.timer.cancel();
-        }
-
-        if (this.policy) {
-          this.policy.stopPolling();
-        }
-        break;
-    }
-  },
-
-  /**
-   * The HealthReporter instance associated with this service.
-   *
-   * If the service is disabled, this will return null.
-   *
-   * The obtained instance may not be fully initialized.
-   */
-  get healthReporter() {
-    if (!this._prefs.get("service.enabled", true)) {
-      return null;
-    }
-
-    if ("_healthReporter" in this) {
-      return this._healthReporter;
-    }
-
-    try {
-      this._loadHealthReporter();
-    } catch (ex) {
-      this._healthReporter = null;
-      Cu.reportError("Exception when obtaining health reporter: " +
-                     Log.exceptionStr(ex));
-    }
-
-    return this._healthReporter;
-  },
-
-  _loadHealthReporter: function () {
-    // This should never happen. It was added to help trace down bug 924307.
-    if (!this.policy) {
-      throw new Error("this.policy not set.");
-    }
-
-    let ns = {};
-    // Lazy import so application startup isn't adversely affected.
-
-    Cu.import("resource://gre/modules/HealthReport.jsm", ns);
-
-    // How many times will we rewrite this code before rolling it up into a
-    // generic module? See also bug 451283.
-    const LOGGERS = [
-      "Services.DataReporting",
-      "Services.HealthReport",
-      "Services.Metrics",
-      "Services.BagheeraClient",
-      "Sqlite.Connection.healthreport",
-    ];
-
-    let loggingPrefs = new Preferences(HEALTHREPORT_LOGGING_BRANCH);
-    if (loggingPrefs.get("consoleEnabled", true)) {
-      let level = loggingPrefs.get("consoleLevel", "Warn");
-      let appender = new Log.ConsoleAppender();
-      appender.level = Log.Level[level] || Log.Level.Warn;
-
-      for (let name of LOGGERS) {
-        let logger = Log.repository.getLogger(name);
-        logger.addAppender(appender);
-      }
-    }
-
-    if (loggingPrefs.get("dumpEnabled", false)) {
-      let level = loggingPrefs.get("dumpLevel", "Debug");
-      let appender = new Log.DumpAppender();
-      appender.level = Log.Level[level] || Log.Level.Debug;
-
-      for (let name of LOGGERS) {
-        let logger = Log.repository.getLogger(name);
-        logger.addAppender(appender);
-      }
-    }
-
-    this._healthReporter = new ns.HealthReporter(HEALTHREPORT_BRANCH, this.policy);
-
-    // Wait for initialization to finish so if a shutdown occurs before init
-    // has finished we don't adversely affect app startup on next run.
-    this._healthReporter.init().then(function onInit() {
-      this._prefs.set("service.firstRun", true);
-    }.bind(this));
-  },
-
-  /**
-   * This returns a promise resolving to the the stable client ID we use for
-   * data reporting (FHR & Telemetry). Previously exising FHR client IDs are
-   * migrated to this.
-   *
-   * @return Promise<string> The stable client ID.
-   */
-  getClientID: function() {
-    return ClientID.getClientID();
-  },
-});
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]);
-
-#define MERGED_COMPARTMENT
-
-#include ../common/observers.js
-;
-#include policy.jsm
-;
-
deleted file mode 100644
--- a/services/datareporting/modules-testing/mocks.jsm
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 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";
-
-this.EXPORTED_SYMBOLS = ["MockPolicyListener"];
-
-const {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-
-
-this.MockPolicyListener = function MockPolicyListener() {
-  this._log = Log.repository.getLogger("Services.DataReporting.Testing.MockPolicyListener");
-  this._log.level = Log.Level["Debug"];
-
-  this.requestDataUploadCount = 0;
-  this.lastDataRequest = null;
-
-  this.requestRemoteDeleteCount = 0;
-  this.lastRemoteDeleteRequest = null;
-
-  this.notifyUserCount = 0;
-  this.lastNotifyRequest = null;
-}
-
-MockPolicyListener.prototype = {
-  onRequestDataUpload: function (request) {
-    this._log.info("onRequestDataUpload invoked.");
-    this.requestDataUploadCount++;
-    this.lastDataRequest = request;
-  },
-
-  onRequestRemoteDelete: function (request) {
-    this._log.info("onRequestRemoteDelete invoked.");
-    this.requestRemoteDeleteCount++;
-    this.lastRemoteDeleteRequest = request;
-  },
-
-  onNotifyDataPolicy: function (request, rejectMessage=null) {
-    this._log.info("onNotifyDataPolicy invoked.");
-    this.notifyUserCount++;
-    this.lastNotifyRequest = request;
-    if (rejectMessage) {
-      request.onUserNotifyFailed(rejectMessage);
-    } else {
-      request.onUserNotifyComplete();
-    }
-  },
-};
-
deleted file mode 100644
--- a/services/datareporting/moz.build
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- 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/.
-
-XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
-
-EXTRA_COMPONENTS += [
-    'DataReporting.manifest',
-]
-
-EXTRA_PP_COMPONENTS += [
-    'DataReportingService.js',
-]
-
-EXTRA_PP_JS_MODULES.services.datareporting += [
-    'policy.jsm',
-]
-
-TESTING_JS_MODULES.services.datareporting += [
-    'modules-testing/mocks.jsm',
-]
deleted file mode 100644
--- a/services/datareporting/policy.jsm
+++ /dev/null
@@ -1,927 +0,0 @@
-/* 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/. */
-
-/**
- * This file is in transition. Most of its content needs to be moved under
- * /services/healthreport.
- */
-
-#ifndef MERGED_COMPARTMENT
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = [
-  "DataSubmissionRequest", // For test use only.
-  "DataReportingPolicy",
-  "DATAREPORTING_POLICY_VERSION",
-];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-#endif
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-common/utils.js");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-
-// The current policy version number. If the version number stored in the prefs
-// is smaller than this, data upload will be disabled until the user is re-notified
-// about the policy changes.
-const DATAREPORTING_POLICY_VERSION = 1;
-
-const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
-
-// Used as a sanity lower bound for dates stored in prefs. This module was
-// implemented in 2012, so any earlier dates indicate an incorrect clock.
-const OLDEST_ALLOWED_YEAR = 2012;
-
-/**
- * Represents a request to display data policy.
- *
- * Receivers of these instances are expected to call one or more of the on*
- * functions when events occur.
- *
- * When one of these requests is received, the first thing a callee should do
- * is present notification to the user of the data policy. When the notice
- * is displayed to the user, the callee should call `onUserNotifyComplete`.
- *
- * If for whatever reason the callee could not display a notice,
- * it should call `onUserNotifyFailed`.
- *
- * @param policy
- *        (DataReportingPolicy) The policy instance this request came from.
- * @param deferred
- *        (deferred) The promise that will be fulfilled when display occurs.
- */
-function NotifyPolicyRequest(policy, deferred) {
-  this.policy = policy;
-  this.deferred = deferred;
-}
-NotifyPolicyRequest.prototype = Object.freeze({
-  /**
-   * Called when the user is notified of the policy.
-   */
-  onUserNotifyComplete: function () {
-    return this.deferred.resolve();
-   },
-
-  /**
-   * Called when there was an error notifying the user about the policy.
-   *
-   * @param error
-   *        (Error) Explains what went wrong.
-   */
-  onUserNotifyFailed: function (error) {
-    return this.deferred.reject(error);
-  },
-});
-
-/**
- * Represents a request to submit data.
- *
- * Instances of this are created when the policy requests data upload or
- * deletion.
- *
- * Receivers are expected to call one of the provided on* functions to signal
- * completion of the request.
- *
- * Instances of this type should not be instantiated outside of this file.
- * Receivers of instances of this type should not attempt to do anything with
- * the instance except call one of the on* methods.
- */
-this.DataSubmissionRequest = function (promise, expiresDate, isDelete) {
-  this.promise = promise;
-  this.expiresDate = expiresDate;
-  this.isDelete = isDelete;
-
-  this.state = null;
-  this.reason = null;
-}
-
-this.DataSubmissionRequest.prototype = Object.freeze({
-  NO_DATA_AVAILABLE: "no-data-available",
-  SUBMISSION_SUCCESS: "success",
-  SUBMISSION_FAILURE_SOFT: "failure-soft",
-  SUBMISSION_FAILURE_HARD: "failure-hard",
-  UPLOAD_IN_PROGRESS: "upload-in-progress",
-
-  /**
-   * No submission was attempted because no data was available.
-   *
-   * In the case of upload, this means there is no data to upload (perhaps
-   * it isn't available yet). In case of remote deletion, it means that there
-   * is no remote data to delete.
-   */
-  onNoDataAvailable: function onNoDataAvailable() {
-    this.state = this.NO_DATA_AVAILABLE;
-    this.promise.resolve(this);
-    return this.promise.promise;
-  },
-
-  /**
-   * Data submission has completed successfully.
-   *
-   * In case of upload, this means the upload completed successfully. In case
-   * of deletion, the data was deleted successfully.
-   *
-   * @param date
-   *        (Date) When data submission occurred.
-   */
-  onSubmissionSuccess: function onSubmissionSuccess(date) {
-    this.state = this.SUBMISSION_SUCCESS;
-    this.submissionDate = date;
-    this.promise.resolve(this);
-    return this.promise.promise;
-  },
-
-  /**
-   * There was a recoverable failure when submitting data.
-   *
-   * Perhaps the server was down. Perhaps the network wasn't available. The
-   * policy may request submission again after a short delay.
-   *
-   * @param reason
-   *        (string) Why the failure occurred. For logging purposes only.
-   */
-  onSubmissionFailureSoft: function onSubmissionFailureSoft(reason=null) {
-    this.state = this.SUBMISSION_FAILURE_SOFT;
-    this.reason = reason;
-    this.promise.resolve(this);
-    return this.promise.promise;
-  },
-
-  /**
-   * There was an unrecoverable failure when submitting data.
-   *
-   * Perhaps the client is misconfigured. Perhaps the server rejected the data.
-   * Attempts at performing submission again will yield the same result. So,
-   * the policy should not try again (until the next day).
-   *
-   * @param reason
-   *        (string) Why the failure occurred. For logging purposes only.
-   */
-  onSubmissionFailureHard: function onSubmissionFailureHard(reason=null) {
-    this.state = this.SUBMISSION_FAILURE_HARD;
-    this.reason = reason;
-    this.promise.resolve(this);
-    return this.promise.promise;
-  },
-
-  /**
-   * The request was aborted because an upload was already in progress.
-   */
-  onUploadInProgress: function (reason=null) {
-    this.state = this.UPLOAD_IN_PROGRESS;
-    this.reason = reason;
-    this.promise.resolve(this);
-    return this.promise.promise;
-  },
-});
-
-/**
- * Manages scheduling of Firefox Health Report data submission.
- *
- * The rules of data submission are as follows:
- *
- *  1. Do not submit data more than once every 24 hours.
- *  2. Try to submit as close to 24 hours apart as possible.
- *  3. Do not submit too soon after application startup so as to not negatively
- *     impact performance at startup.
- *  4. Before first ever data submission, the user should be notified about
- *     data collection practices.
- *  5. User should have opportunity to react to this notification before
- *     data submission.
- *  6. If data submission fails, try at most 2 additional times before giving
- *     up on that day's submission.
- *
- * The listener passed into the instance must have the following properties
- * (which are callbacks that will be invoked at certain key events):
- *
- *   * onRequestDataUpload(request) - Called when the policy is requesting
- *     data to be submitted. The function is passed a `DataSubmissionRequest`.
- *     The listener should call one of the special resolving functions on that
- *     instance (see the documentation for that type).
- *
- *   * onRequestRemoteDelete(request) - Called when the policy is requesting
- *     deletion of remotely stored data. The function is passed a
- *     `DataSubmissionRequest`. The listener should call one of the special
- *     resolving functions on that instance (just like `onRequestDataUpload`).
- *
- *   * onNotifyDataPolicy(request) - Called when the policy is requesting the
- *     user to be notified that data submission will occur. The function
- *     receives a `NotifyPolicyRequest` instance. The callee should call one or
- *     more of the functions on that instance when specific events occur. See
- *     the documentation for that type for more.
- *
- * Note that the notification method is abstracted. Different applications
- * can have different mechanisms by which they notify the user of data
- * submission practices.
- *
- * @param policyPrefs
- *        (Preferences) Handle on preferences branch on which state will be
- *        queried and stored.
- * @param healthReportPrefs
- *        (Preferences) Handle on preferences branch holding Health Report state.
- * @param listener
- *        (object) Object with callbacks that will be invoked at certain key
- *        events.
- */
-this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) {
-  this._log = Log.repository.getLogger("Services.DataReporting.Policy");
-  this._log.level = Log.Level["Debug"];
-
-  for (let handler of this.REQUIRED_LISTENERS) {
-    if (!listener[handler]) {
-      throw new Error("Passed listener does not contain required handler: " +
-                      handler);
-    }
-  }
-
-  this._prefs = prefs;
-  this._healthReportPrefs = healthReportPrefs;
-  this._listener = listener;
-  this._userNotifyPromise = null;
-
-  this._migratePrefs();
-
-  if (!this.firstRunDate.getTime()) {
-    // If we've never run before, record the current time.
-    this.firstRunDate = this.now();
-  }
-
-  // Install an observer so that we can act on changes from external
-  // code (such as Android UI).
-  // Use a function because this is the only place where the Preferences
-  // abstraction is way less usable than nsIPrefBranch.
-  //
-  // Hang on to the observer here so that tests can reach it.
-  this.uploadEnabledObserver = function onUploadEnabledChanged() {
-    if (this.pendingDeleteRemoteData || this.healthReportUploadEnabled) {
-      // Nothing to do: either we're already deleting because the caller
-      // came through the front door (rHRUE), or they set the flag to true.
-      return;
-    }
-    this._log.info("uploadEnabled pref changed. Scheduling deletion.");
-    this.deleteRemoteData();
-  }.bind(this);
-
-  healthReportPrefs.observe("uploadEnabled", this.uploadEnabledObserver);
-
-  // Ensure we are scheduled to submit.
-  if (!this.nextDataSubmissionDate.getTime()) {
-    this.nextDataSubmissionDate = this._futureDate(MILLISECONDS_PER_DAY);
-  }
-
-  // Record when we last requested for submitted data to be sent. This is
-  // to avoid having multiple outstanding requests.
-  this._inProgressSubmissionRequest = null;
-};
-
-this.DataReportingPolicy.prototype = Object.freeze({
-  /**
-   *  How often to poll to see if we need to do something.
-   *
-   * The interval needs to be short enough such that short-lived applications
-   * have an opportunity to submit data. But, it also needs to be long enough
-   * to not negatively impact performance.
-   *
-   * The random bit is to ensure that other systems scheduling around the same
-   * interval don't all get scheduled together.
-   */
-  POLL_INTERVAL_MSEC: (60 * 1000) + Math.floor(2.5 * 1000 * Math.random()),
-
-  /**
-   * How long individual data submission requests live before expiring.
-   *
-   * Data submission requests have this long to complete before we give up on
-   * them and try again.
-   *
-   * We want this to be short enough that we retry frequently enough but long
-   * enough to give slow networks and systems time to handle it.
-   */
-  SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC: 10 * 60 * 1000,
-
-  /**
-   * Our backoff schedule in case of submission failure.
-   *
-   * This dictates both the number of times we retry a daily submission and
-   * when to retry after each failure.
-   *
-   * Each element represents how long to wait after each recoverable failure.
-   * After the first failure, we wait the time in element 0 before trying
-   * again. After the second failure, we wait the time in element 1. Once
-   * we run out of values in this array, we give up on that day's submission
-   * and schedule for a day out.
-   */
-  FAILURE_BACKOFF_INTERVALS: [
-    15 * 60 * 1000,
-    60 * 60 * 1000,
-  ],
-
-  REQUIRED_LISTENERS: [
-    "onRequestDataUpload",
-    "onRequestRemoteDelete",
-    "onNotifyDataPolicy",
-  ],
-
-  /**
-   * The first time the health report policy came into existence.
-   *
-   * This is used for scheduling of the initial submission.
-   */
-  get firstRunDate() {
-    return CommonUtils.getDatePref(this._prefs, "firstRunTime", 0, this._log,
-                                   OLDEST_ALLOWED_YEAR);
-  },
-
-  set firstRunDate(value) {
-    this._log.debug("Setting first-run date: " + value);
-    CommonUtils.setDatePref(this._prefs, "firstRunTime", value,
-                            OLDEST_ALLOWED_YEAR);
-  },
-
-  get dataSubmissionPolicyNotifiedDate() {
-    return CommonUtils.getDatePref(this._prefs,
-                                   "dataSubmissionPolicyNotifiedTime", 0,
-                                   this._log, OLDEST_ALLOWED_YEAR);
-  },
-
-  set dataSubmissionPolicyNotifiedDate(value) {
-    this._log.debug("Setting user notified date: " + value);
-    CommonUtils.setDatePref(this._prefs, "dataSubmissionPolicyNotifiedTime",
-                            value, OLDEST_ALLOWED_YEAR);
-  },
-
-  get dataSubmissionPolicyBypassNotification() {
-    return this._prefs.get("dataSubmissionPolicyBypassNotification", false);
-  },
-
-  set dataSubmissionPolicyBypassNotification(value) {
-    return this._prefs.set("dataSubmissionPolicyBypassNotification", !!value);
-  },
-
-  /**
-   * Whether submission of data is allowed.
-   *
-   * This is the master switch for remote server communication. If it is
-   * false, we never request upload or deletion.
-   */
-  get dataSubmissionEnabled() {
-    // Default is true because we are opt-out.
-    return this._prefs.get("dataSubmissionEnabled", true);
-  },
-
-  set dataSubmissionEnabled(value) {
-    this._prefs.set("dataSubmissionEnabled", !!value);
-  },
-
-  /**
-   * Whether submission of data is allowed for v2.
-   *
-   * This is used to gently turn off data submission for FHR v2 in Firefox 42+.
-   */
-  get dataSubmissionEnabledV2() {
-    // Default is true because we are opt-out.
-    return this._prefs.get("dataSubmissionEnabled.v2", true);
-  },
-
-  get currentPolicyVersion() {
-    return this._prefs.get("currentPolicyVersion", DATAREPORTING_POLICY_VERSION);
-  },
-
-  /**
-   * The minimum policy version which for dataSubmissionPolicyAccepted to
-   * to be valid.
-   */
-  get minimumPolicyVersion() {
-    // First check if the current channel has an ove
-    let channel = UpdateUtils.getUpdateChannel(false);
-    let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel);
-    return channelPref !== undefined ?
-           channelPref : this._prefs.get("minimumPolicyVersion", 1);
-  },
-
-  get dataSubmissionPolicyAcceptedVersion() {
-    return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0);
-  },
-
-  set dataSubmissionPolicyAcceptedVersion(value) {
-    this._prefs.set("dataSubmissionPolicyAcceptedVersion", value);
-  },
-
-  /**
-   * Checks to see if the user has been notified about data submission
-   * @return {bool}
-   */
-  get userNotifiedOfCurrentPolicy() {
-    return  this.dataSubmissionPolicyNotifiedDate.getTime() > 0 &&
-            this.dataSubmissionPolicyAcceptedVersion >= this.currentPolicyVersion;
-  },
-
-  /**
-   * When this policy last requested data submission.
-   *
-   * This is used mainly for forensics purposes and should have no bearing
-   * on scheduling or run-time behavior.
-   */
-  get lastDataSubmissionRequestedDate() {
-    return CommonUtils.getDatePref(this._healthReportPrefs,
-                                   "lastDataSubmissionRequestedTime", 0,
-                                   this._log, OLDEST_ALLOWED_YEAR);
-  },
-
-  set lastDataSubmissionRequestedDate(value) {
-    CommonUtils.setDatePref(this._healthReportPrefs,
-                            "lastDataSubmissionRequestedTime",
-                            value, OLDEST_ALLOWED_YEAR);
-  },
-
-  /**
-   * When the last data submission actually occurred.
-   *
-   * This is used mainly for forensics purposes and should have no bearing on
-   * actual scheduling.
-   */
-  get lastDataSubmissionSuccessfulDate() {
-    return CommonUtils.getDatePref(this._healthReportPrefs,
-                                   "lastDataSubmissionSuccessfulTime", 0,
-                                   this._log, OLDEST_ALLOWED_YEAR);
-  },
-
-  set lastDataSubmissionSuccessfulDate(value) {
-    CommonUtils.setDatePref(this._healthReportPrefs,
-                            "lastDataSubmissionSuccessfulTime",
-                            value, OLDEST_ALLOWED_YEAR);
-  },
-
-  /**
-   * When we last encountered a submission failure.
-   *
-   * This is used for forensics purposes and should have no bearing on
-   * scheduling.
-   */
-  get lastDataSubmissionFailureDate() {
-    return CommonUtils.getDatePref(this._healthReportPrefs,
-                                   "lastDataSubmissionFailureTime",
-                                   0, this._log, OLDEST_ALLOWED_YEAR);
-  },
-
-  set lastDataSubmissionFailureDate(value) {
-    CommonUtils.setDatePref(this._healthReportPrefs,
-                            "lastDataSubmissionFailureTime",
-                            value, OLDEST_ALLOWED_YEAR);
-  },
-
-  /**
-   * When the next data submission is scheduled to occur.
-   *
-   * This is maintained internally by this type. External users should not
-   * mutate this value.
-   */
-  get nextDataSubmissionDate() {
-    return CommonUtils.getDatePref(this._healthReportPrefs,
-                                   "nextDataSubmissionTime", 0,
-                                   this._log, OLDEST_ALLOWED_YEAR);
-  },
-
-  set nextDataSubmissionDate(value) {
-    CommonUtils.setDatePref(this._healthReportPrefs,
-                            "nextDataSubmissionTime", value,
-                            OLDEST_ALLOWED_YEAR);
-  },
-
-  /**
-   * The number of submission failures for this day's upload.
-   *
-   * This is used to drive backoff and scheduling.
-   */
-  get currentDaySubmissionFailureCount() {
-    let v = this._healthReportPrefs.get("currentDaySubmissionFailureCount", 0);
-
-    if (!Number.isInteger(v)) {
-      v = 0;
-    }
-
-    return v;
-  },
-
-  set currentDaySubmissionFailureCount(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("Value must be integer: " + value);
-    }
-
-    this._healthReportPrefs.set("currentDaySubmissionFailureCount", value);
-  },
-
-  /**
-   * Whether a request to delete remote data is awaiting completion.
-   *
-   * If this is true, the policy will request that remote data be deleted.
-   * Furthermore, no new data will be uploaded (if it's even allowed) until
-   * the remote deletion is fulfilled.
-   */
-  get pendingDeleteRemoteData() {
-    return !!this._healthReportPrefs.get("pendingDeleteRemoteData", false);
-  },
-
-  set pendingDeleteRemoteData(value) {
-    this._healthReportPrefs.set("pendingDeleteRemoteData", !!value);
-  },
-
-  /**
-   * Whether upload of Firefox Health Report data is enabled.
-   */
-  get healthReportUploadEnabled() {
-    return !!this._healthReportPrefs.get("uploadEnabled", true);
-  },
-
-  // External callers should update this via `recordHealthReportUploadEnabled`
-  // to ensure appropriate side-effects are performed.
-  set healthReportUploadEnabled(value) {
-    this._healthReportPrefs.set("uploadEnabled", !!value);
-  },
-
-  /**
-   * Whether the FHR upload enabled setting is locked and can't be changed.
-   */
-  get healthReportUploadLocked() {
-    return this._healthReportPrefs.locked("uploadEnabled");
-  },
-
-  /**
-   * Record the user's intent for whether FHR should upload data.
-   *
-   * This is the preferred way for XUL applications to record a user's
-   * preference on whether Firefox Health Report should upload data to
-   * a server.
-   *
-   * If upload is disabled through this API, a request for remote data
-   * deletion is initiated automatically.
-   *
-   * If upload is being disabled and this operation is scheduled to
-   * occur immediately, a promise will be returned. This promise will be
-   * fulfilled when the deletion attempt finishes. If upload is being
-   * disabled and a promise is not returned, callers must poll
-   * `haveRemoteData` on the HealthReporter instance to see if remote
-   * data has been deleted.
-   *
-   * @param flag
-   *        (bool) Whether data submission is enabled or disabled.
-   * @param reason
-   *        (string) Why this value is being adjusted. For logging
-   *        purposes only.
-   */
-  recordHealthReportUploadEnabled: function (flag, reason="no-reason") {
-    let result = null;
-    if (!flag) {
-      result = this.deleteRemoteData(reason);
-    }
-
-    this.healthReportUploadEnabled = flag;
-    return result;
-  },
-
-  /**
-   * Request that remote data be deleted.
-   *
-   * This will record an intent that previously uploaded data is to be deleted.
-   * The policy will eventually issue a request to the listener for data
-   * deletion. It will keep asking for deletion until the listener acknowledges
-   * that data has been deleted.
-   */
-  deleteRemoteData: function deleteRemoteData(reason="no-reason") {
-    this._log.info("Remote data deletion requested: " + reason);
-
-    this.pendingDeleteRemoteData = true;
-
-    // We want delete deletion to occur as soon as possible. Move up any
-    // pending scheduled data submission and try to trigger.
-    this.nextDataSubmissionDate = this.now();
-    return this.checkStateAndTrigger();
-  },
-
-  /**
-   * Start background polling for activity.
-   *
-   * This will set up a recurring timer that will periodically check if
-   * activity is warranted.
-   *
-   * You typically call this function for each constructed instance.
-   */
-  startPolling: function startPolling() {
-    this.stopPolling();
-
-    this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this._timer.initWithCallback({
-      notify: function notify() {
-        this.checkStateAndTrigger();
-      }.bind(this)
-    }, this.POLL_INTERVAL_MSEC, this._timer.TYPE_REPEATING_SLACK);
-  },
-
-  /**
-   * Stop background polling for activity.
-   *
-   * This should be called when the instance is no longer needed.
-   */
-  stopPolling: function stopPolling() {
-    if (this._timer) {
-      this._timer.cancel();
-      this._timer = null;
-    }
-  },
-
-  /**
-   * Abstraction for obtaining current time.
-   *
-   * The purpose of this is to facilitate testing. Testing code can monkeypatch
-   * this on instances instead of modifying the singleton Date object.
-   */
-  now: function now() {
-    return new Date();
-  },
-
-  /**
-   * Check state and trigger actions, if necessary.
-   *
-   * This is what enforces the submission and notification policy detailed
-   * above. You can think of this as the driver for health report data
-   * submission.
-   *
-   * Typically this function is called automatically by the background polling.
-   * But, it can safely be called manually as needed.
-   */
-  checkStateAndTrigger: function checkStateAndTrigger() {
-    // If the master data submission kill switch is toggled, we have nothing
-    // to do. We don't notify about data policies because this would have
-    // no effect.
-    if (!this.dataSubmissionEnabled || !this.dataSubmissionEnabledV2) {
-      this._log.debug("Data submission is disabled. Doing nothing.");
-      return;
-    }
-
-    let now = this.now();
-    let nowT = now.getTime();
-    let nextSubmissionDate = this.nextDataSubmissionDate;
-
-    // If the system clock were ever set to a time in the distant future,
-    // it's possible our next schedule date is far out as well. We know
-    // we shouldn't schedule for more than a day out, so we reset the next
-    // scheduled date appropriately. 3 days was chosen arbitrarily.
-    if (nextSubmissionDate.getTime() >= nowT + 3 * MILLISECONDS_PER_DAY) {
-      this._log.warn("Next data submission time is far away. Was the system " +
-                     "clock recently readjusted? " + nextSubmissionDate);
-
-      // It shouldn't really matter what we set this to. 1 day in the future
-      // should be pretty safe.
-      this._moveScheduleForward24h();
-
-      // Fall through since we may have other actions.
-    }
-
-    // Tend to any in progress work.
-    if (this._processInProgressSubmission()) {
-      return;
-    }
-
-    // Requests to delete remote data take priority above everything else.
-    if (this.pendingDeleteRemoteData) {
-      if (nowT < nextSubmissionDate.getTime()) {
-        this._log.debug("Deletion request is scheduled for the future: " +
-                        nextSubmissionDate);
-        return;
-      }
-
-      return this._dispatchSubmissionRequest("onRequestRemoteDelete", true);
-    }
-
-    if (!this.healthReportUploadEnabled) {
-      this._log.debug("Data upload is disabled. Doing nothing.");
-      return;
-    }
-
-    if (!this.ensureUserNotified()) {
-      this._log.warn("The user has not been notified about the data submission " +
-                     "policy. Not attempting upload.");
-      return;
-    }
-
-    // Data submission is allowed to occur. Now comes the scheduling part.
-
-    if (nowT < nextSubmissionDate.getTime()) {
-      this._log.debug("Next data submission is scheduled in the future: " +
-                     nextSubmissionDate);
-      return;
-    }
-
-    return this._dispatchSubmissionRequest("onRequestDataUpload", false);
-  },
-
-  /**
-   * Ensure that the data policy notification has been displayed.
-   *
-   * This must be called before data submission. If the policy has not been
-   * displayed, data submission must not occur.
-   *
-   * @return bool Whether the notification has been displayed.
-   */
-  ensureUserNotified: function () {
-    if (this.userNotifiedOfCurrentPolicy || this.dataSubmissionPolicyBypassNotification) {
-      return true;
-    }
-
-    // The user has not been notified yet, but is in the process of being notified.
-    if (this._userNotifyPromise) {
-      return false;
-    }
-
-    let deferred = Promise.defer();
-    deferred.promise.then((function onSuccess() {
-      this._recordDataPolicyNotification(this.now(), this.currentPolicyVersion);
-      this._userNotifyPromise = null;
-    }).bind(this), ((error) => {
-      this._log.warn("Data policy notification presentation failed", error);
-      this._userNotifyPromise = null;
-    }).bind(this));
-
-    this._log.info("Requesting display of data policy.");
-    let request = new NotifyPolicyRequest(this, deferred);
-    try {
-      this._listener.onNotifyDataPolicy(request);
-    } catch (ex) {
-      this._log.warn("Exception when calling onNotifyDataPolicy", ex);
-    }
-
-    this._userNotifyPromise = deferred.promise;
-
-    return false;
-  },
-
-  _recordDataPolicyNotification: function (date, version) {
-    this._log.debug("Recording data policy notification to version " + version +
-                  " on date " + date);
-    this.dataSubmissionPolicyNotifiedDate = date;
-    this.dataSubmissionPolicyAcceptedVersion = version;
-  },
-
-  _migratePrefs: function () {
-    // Current prefs are mostly the same than the old ones, except for some deprecated ones.
-    this._prefs.reset([
-      "dataSubmissionPolicyAccepted",
-      "dataSubmissionPolicyBypassAcceptance",
-      "dataSubmissionPolicyResponseType",
-      "dataSubmissionPolicyResponseTime"
-    ]);
-  },
-
-  _processInProgressSubmission: function _processInProgressSubmission() {
-    if (!this._inProgressSubmissionRequest) {
-      return false;
-    }
-
-    let now = this.now().getTime();
-    if (this._inProgressSubmissionRequest.expiresDate.getTime() > now) {
-      this._log.info("Waiting on in-progress submission request to finish.");
-      return true;
-    }
-
-    this._log.warn("Old submission request has expired from no activity.");
-    this._inProgressSubmissionRequest.promise.reject(new Error("Request has expired."));
-    this._inProgressSubmissionRequest = null;
-    this._handleSubmissionFailure();
-
-    return false;
-  },
-
-  _dispatchSubmissionRequest: function _dispatchSubmissionRequest(handler, isDelete) {
-    let now = this.now();
-
-    // We're past our scheduled next data submission date, so let's do it!
-    this.lastDataSubmissionRequestedDate = now;
-    let deferred = Promise.defer();
-    let requestExpiresDate =
-      this._futureDate(this.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC);
-    this._inProgressSubmissionRequest = new DataSubmissionRequest(deferred,
-                                                                  requestExpiresDate,
-                                                                  isDelete);
-
-    let onSuccess = function onSuccess(result) {
-      this._inProgressSubmissionRequest = null;
-      this._handleSubmissionResult(result);
-    }.bind(this);
-
-    let onError = function onError(error) {
-      this._log.error("Error when handling data submission result", error);
-      this._inProgressSubmissionRequest = null;
-      this._handleSubmissionFailure();
-    }.bind(this);
-
-    let chained = deferred.promise.then(onSuccess, onError);
-
-    this._log.info("Requesting data submission. Will expire at " +
-                   requestExpiresDate);
-    try {
-      let promise = this._listener[handler](this._inProgressSubmissionRequest);
-      chained = chained.then(() => promise, null);
-    } catch (ex) {
-      this._log.warn("Exception when calling " + handler, ex);
-      this._inProgressSubmissionRequest = null;
-      this._handleSubmissionFailure();
-      return;
-    }
-
-    return chained;
-  },
-
-  _handleSubmissionResult: function _handleSubmissionResult(request) {
-    let state = request.state;
-    let reason = request.reason || "no reason";
-    this._log.info("Got submission request result: " + state);
-
-    if (state == request.SUBMISSION_SUCCESS) {
-      if (request.isDelete) {
-        this.pendingDeleteRemoteData = false;
-        this._log.info("Successful data delete reported.");
-      } else {
-        this._log.info("Successful data upload reported.");
-      }
-
-      this.lastDataSubmissionSuccessfulDate = request.submissionDate;
-
-      let nextSubmissionDate =
-        new Date(request.submissionDate.getTime() + MILLISECONDS_PER_DAY);
-
-      // Schedule pending deletes immediately. This has potential to overload
-      // the server. However, the frequency of delete requests across all
-      // clients should be low, so this shouldn't pose a problem.
-      if (this.pendingDeleteRemoteData) {
-        nextSubmissionDate = this.now();
-      }
-
-      this.nextDataSubmissionDate = nextSubmissionDate;
-      this.currentDaySubmissionFailureCount = 0;
-      return;
-    }
-
-    if (state == request.NO_DATA_AVAILABLE) {
-      if (request.isDelete) {
-        this._log.info("Remote data delete requested but no remote data was stored.");
-        this.pendingDeleteRemoteData = false;
-        return;
-      }
-
-      this._log.info("No data was available to submit. May try later.");
-      this._handleSubmissionFailure();
-      return;
-    }
-
-    // We don't special case request.isDelete for these failures because it
-    // likely means there was a server error.
-
-    if (state == request.SUBMISSION_FAILURE_SOFT) {
-      this._log.warn("Soft error submitting data: " + reason);
-      this.lastDataSubmissionFailureDate = this.now();
-      this._handleSubmissionFailure();
-      return;
-    }
-
-    if (state == request.SUBMISSION_FAILURE_HARD) {
-      this._log.warn("Hard error submitting data: " + reason);
-      this.lastDataSubmissionFailureDate = this.now();
-      this._moveScheduleForward24h();
-      return;
-    }
-
-    throw new Error("Unknown state on DataSubmissionRequest: " + request.state);
-  },
-
-  _handleSubmissionFailure: function _handleSubmissionFailure() {
-    if (this.currentDaySubmissionFailureCount >= this.FAILURE_BACKOFF_INTERVALS.length) {
-      this._log.warn("Reached the limit of daily submission attempts. " +
-                     "Rescheduling for tomorrow.");
-      this._moveScheduleForward24h();
-      return false;
-    }
-
-    let offset = this.FAILURE_BACKOFF_INTERVALS[this.currentDaySubmissionFailureCount];
-    this.nextDataSubmissionDate = this._futureDate(offset);
-    this.currentDaySubmissionFailureCount++;
-    return true;
-  },
-
-  _moveScheduleForward24h: function _moveScheduleForward24h() {
-    let d = this._futureDate(MILLISECONDS_PER_DAY);
-    this._log.info("Setting next scheduled data submission for " + d);
-
-    this.nextDataSubmissionDate = d;
-    this.currentDaySubmissionFailureCount = 0;
-  },
-
-  _futureDate: function _futureDate(offset) {
-    return new Date(this.now().getTime() + offset);
-  },
-});
-
deleted file mode 100644
--- a/services/datareporting/tests/xpcshell/head.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// We need to initialize the profile or OS.File may not work. See bug 810543.
-do_get_profile();
-
-(function initTestingInfrastructure() {
-  let ns = {};
-  Components.utils.import("resource://testing-common/services/common/logging.js",
-                          ns);
-
-  ns.initTestLogging();
-}).call(this);
-
deleted file mode 100644
--- a/services/datareporting/tests/xpcshell/test_policy.js
+++ /dev/null
@@ -1,689 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
-Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-
-function getPolicy(name,
-                   aCurrentPolicyVersion = 1,
-                   aMinimumPolicyVersion = 1,
-                   aBranchMinimumVersionOverride) {
-  let branch = "testing.datareporting." + name;
-
-  // The version prefs should not be removed on reset, so set them in the
-  // default branch.
-  let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy."
-                                           , defaultBranch: true });
-  defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion);
-  defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion);
-  let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateUtils.getUpdateChannel(false);
-  if (aBranchMinimumVersionOverride !== undefined)
-    defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride);
-  else
-    defaultPolicyPrefs.reset(branchOverridePrefName);
-
-  let policyPrefs = new Preferences(branch + ".policy.");
-  let healthReportPrefs = new Preferences(branch + ".healthreport.");
-
-  let listener = new MockPolicyListener();
-  let policy = new DataReportingPolicy(policyPrefs, healthReportPrefs, listener);
-
-  return [policy, policyPrefs, healthReportPrefs, listener];
-}
-
-/**
- * Ensure that the notification has been displayed to the user therefore having
- * policy.ensureUserNotified() === true, which will allow for a successful
- * data upload and afterwards does a call to policy.checkStateAndTrigger()
- * @param  {Policy} policy
- * @return {Promise}
- */
-function ensureUserNotifiedAndTrigger(policy) {
-  return Task.spawn(function* ensureUserNotifiedAndTrigger () {
-    policy.ensureUserNotified();
-    yield policy._listener.lastNotifyRequest.deferred.promise;
-    do_check_true(policy.userNotifiedOfCurrentPolicy);
-    policy.checkStateAndTrigger();
-  });
-}
-
-function defineNow(policy, now) {
-  print("Adjusting fake system clock to " + now);
-  Object.defineProperty(policy, "now", {
-    value: function customNow() {
-      return now;
-    },
-    writable: true,
-  });
-}
-
-function run_test() {
-  run_next_test();
-}
-
-add_test(function test_constructor() {
-  let policyPrefs = new Preferences("foo.bar.policy.");
-  let hrPrefs = new Preferences("foo.bar.healthreport.");
-  let listener = {
-    onRequestDataUpload: function() {},
-    onRequestRemoteDelete: function() {},
-    onNotifyDataPolicy: function() {},
-  };
-
-  let policy = new DataReportingPolicy(policyPrefs, hrPrefs, listener);
-  do_check_true(Date.now() - policy.firstRunDate.getTime() < 1000);
-
-  let tomorrow = Date.now() + 24 * 60 * 60 * 1000;
-  do_check_true(tomorrow - policy.nextDataSubmissionDate.getTime() < 1000);
-
-  do_check_eq(policy.dataSubmissionPolicyAcceptedVersion, 0);
-  do_check_false(policy.userNotifiedOfCurrentPolicy);
-
-  run_next_test();
-});
-
-add_test(function test_prefs() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("prefs");
-
-  let now = new Date();
-  let nowT = now.getTime();
-
-  policy.firstRunDate = now;
-  do_check_eq(policyPrefs.get("firstRunTime"), nowT);
-  do_check_eq(policy.firstRunDate.getTime(), nowT);
-
-  policy.dataSubmissionPolicyNotifiedDate = now;
-  do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT);
-  do_check_neq(policy.dataSubmissionPolicyNotifiedDate, null);
-  do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT);
-
-  policy.dataSubmissionEnabled = false;
-  do_check_false(policyPrefs.get("dataSubmissionEnabled", true));
-  do_check_false(policy.dataSubmissionEnabled);
-
-  let new_version = DATAREPORTING_POLICY_VERSION + 1;
-  policy.dataSubmissionPolicyAcceptedVersion = new_version;
-  do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), new_version);
-
-  do_check_false(policy.dataSubmissionPolicyBypassNotification);
-  policy.dataSubmissionPolicyBypassNotification = true;
-  do_check_true(policy.dataSubmissionPolicyBypassNotification);
-  do_check_true(policyPrefs.get("dataSubmissionPolicyBypassNotification"));
-
-  policy.lastDataSubmissionRequestedDate = now;
-  do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT);
-  do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), nowT);
-
-  policy.lastDataSubmissionSuccessfulDate = now;
-  do_check_eq(hrPrefs.get("lastDataSubmissionSuccessfulTime"), nowT);
-  do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), nowT);
-
-  policy.lastDataSubmissionFailureDate = now;
-  do_check_eq(hrPrefs.get("lastDataSubmissionFailureTime"), nowT);
-  do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), nowT);
-
-  policy.nextDataSubmissionDate = now;
-  do_check_eq(hrPrefs.get("nextDataSubmissionTime"), nowT);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(), nowT);
-
-  policy.currentDaySubmissionFailureCount = 2;
-  do_check_eq(hrPrefs.get("currentDaySubmissionFailureCount", 0), 2);
-  do_check_eq(policy.currentDaySubmissionFailureCount, 2);
-
-  policy.pendingDeleteRemoteData = true;
-  do_check_true(hrPrefs.get("pendingDeleteRemoteData"));
-  do_check_true(policy.pendingDeleteRemoteData);
-
-  policy.healthReportUploadEnabled = false;
-  do_check_false(hrPrefs.get("uploadEnabled"));
-  do_check_false(policy.healthReportUploadEnabled);
-
-  do_check_false(policy.healthReportUploadLocked);
-  hrPrefs.lock("uploadEnabled");
-  do_check_true(policy.healthReportUploadLocked);
-  hrPrefs.unlock("uploadEnabled");
-  do_check_false(policy.healthReportUploadLocked);
-
-  run_next_test();
-});
-
-add_task(function test_migratePrefs () {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("migratePrefs");
-  let outdated_prefs = {
-    dataSubmissionPolicyAccepted: true,
-    dataSubmissionPolicyBypassAcceptance: true,
-    dataSubmissionPolicyResponseType: "something",
-    dataSubmissionPolicyResponseTime: Date.now() + "",
-  };
-
-  // Test removal of old prefs.
-  for (let name in outdated_prefs) {
-    policyPrefs.set(name, outdated_prefs[name]);
-  }
-  policy._migratePrefs();
-  for (let name in outdated_prefs) {
-    do_check_false(policyPrefs.has(name));
-  }
-});
-
-add_task(function test_userNotifiedOfCurrentPolicy () {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification");
-
-  do_check_false(policy.userNotifiedOfCurrentPolicy,
-                 "The initial state should be unnotified.");
-  do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
-
-  policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION;
-  do_check_false(policy.userNotifiedOfCurrentPolicy,
-                 "The default state of the date should have a time of 0 and it should therefore fail");
-  do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0,
-              "Updating the accepted version should not set a notified date.");
-
-  policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
-  do_check_true(policy.userNotifiedOfCurrentPolicy,
-                "Using the proper API causes user notification to report as true.");
-
-  // It is assumed that later versions of the policy will incorporate previous
-  // ones, therefore this should also return true.
-  policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
-  policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION + 1;
-  do_check_true(policy.userNotifiedOfCurrentPolicy, 'A future version of the policy should pass.');
-
-  policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
-  policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION - 1;
-  do_check_false(policy.userNotifiedOfCurrentPolicy, 'A previous version of the policy should fail.');
-});
-
-add_task(function* test_notification_displayed () {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accept_displayed");
-
-  do_check_eq(listener.requestDataUploadCount, 0);
-  do_check_eq(listener.notifyUserCount, 0);
-  do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
-
-  // Uploads will trigger user notifications as needed.
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.notifyUserCount, 1);
-  do_check_eq(listener.requestDataUploadCount, 0);
-
-  yield ensureUserNotifiedAndTrigger(policy);
-
-  do_check_eq(listener.notifyUserCount, 1);
-  do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0);
-  do_check_true(policy.userNotifiedOfCurrentPolicy);
-});
-
-add_task(function* test_submission_kill_switch() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch");
-  policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 0);
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-
-  defineNow(policy,
-    new Date(Date.now() + policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC + 100));
-  policy.dataSubmissionEnabled = false;
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-});
-
-add_task(function* test_upload_kill_switch() {
-   let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch");
-
-  yield ensureUserNotifiedAndTrigger(policy);
-  defineNow(policy, policy.nextDataSubmissionDate);
-
-  // So that we don't trigger deletions, which cause uploads to be delayed.
-  hrPrefs.ignore("uploadEnabled", policy.uploadEnabledObserver);
-
-  policy.healthReportUploadEnabled = false;
-  yield policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 0);
-  policy.healthReportUploadEnabled = true;
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-});
-
-add_task(function* test_data_submission_no_data() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data");
-
-  let now = new Date(policy.nextDataSubmissionDate.getTime() + 1);
-  defineNow(policy, now);
-  do_check_eq(listener.requestDataUploadCount, 0);
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  listener.lastDataRequest.onNoDataAvailable();
-
-  // The next trigger should try again.
-  defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 2);
- });
-
-add_task(function* test_data_submission_submit_failure_hard() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard");
-
-  let nextDataSubmissionDate = policy.nextDataSubmissionDate;
-  let now = new Date(policy.nextDataSubmissionDate.getTime() + 1);
-  defineNow(policy, now);
-
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  yield listener.lastDataRequest.onSubmissionFailureHard();
-  do_check_eq(listener.lastDataRequest.state,
-              listener.lastDataRequest.SUBMISSION_FAILURE_HARD);
-
-  let expected = new Date(now.getTime() + 24 * 60 * 60 * 1000);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(), expected.getTime());
-
-  defineNow(policy, new Date(now.getTime() + 10));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-});
-
-add_task(function* test_data_submission_submit_try_again() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft");
-
-  let nextDataSubmissionDate = policy.nextDataSubmissionDate;
-  let now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  yield ensureUserNotifiedAndTrigger(policy);
-  yield listener.lastDataRequest.onSubmissionFailureSoft();
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-              nextDataSubmissionDate.getTime() + 15 * 60 * 1000);
-});
-
-add_task(function* test_submission_daily_scheduling() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling");
-
-  let nextDataSubmissionDate = policy.nextDataSubmissionDate;
-
-  // Skip ahead to next submission date. We should get a submission request.
-  let now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime());
-
-  let finishedDate = new Date(now.getTime() + 250);
-  defineNow(policy, new Date(finishedDate.getTime() + 50));
-  yield listener.lastDataRequest.onSubmissionSuccess(finishedDate);
-  do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), finishedDate.getTime());
-
-  // Next scheduled submission should be exactly 1 day after the reported
-  // submission success.
-
-  let nextScheduled = new Date(finishedDate.getTime() + 24 * 60 * 60 * 1000);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(), nextScheduled.getTime());
-
-  // Fast forward some arbitrary time. We shouldn't do any work yet.
-  defineNow(policy, new Date(now.getTime() + 40000));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-
-  defineNow(policy, nextScheduled);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 2);
-  yield listener.lastDataRequest.onSubmissionSuccess(new Date(nextScheduled.getTime() + 200));
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-    new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime());
-});
-
-add_task(function* test_submission_far_future_scheduling() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling");
-
-  let now = new Date(Date.now() - 24 * 60 * 60 * 1000);
-  defineNow(policy, now);
-  yield ensureUserNotifiedAndTrigger(policy);
-
-  let nextDate = policy._futureDate(3 * 24 * 60 * 60 * 1000 - 1);
-  policy.nextDataSubmissionDate = nextDate;
-  policy.checkStateAndTrigger();
-  do_check_true(policy.dataSubmissionPolicyAcceptedVersion >= DATAREPORTING_POLICY_VERSION);
-  do_check_eq(listener.requestDataUploadCount, 0);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(), nextDate.getTime());
-
-  policy.nextDataSubmissionDate = new Date(nextDate.getTime() + 1);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 0);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-              policy._futureDate(24 * 60 * 60 * 1000).getTime());
-});
-
-add_task(function* test_submission_backoff() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff");
-
-  do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2);
-
-
-  let now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  do_check_eq(policy.currentDaySubmissionFailureCount, 0);
-
-  now = new Date(now.getTime() + 5000);
-  defineNow(policy, now);
-
-  // On first soft failure we should back off by scheduled interval.
-  yield listener.lastDataRequest.onSubmissionFailureSoft();
-  do_check_eq(policy.currentDaySubmissionFailureCount, 1);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-              new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[0]).getTime());
-  do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), now.getTime());
-
-  // Should not request submission until scheduled.
-  now = new Date(policy.nextDataSubmissionDate.getTime() - 1);
-  defineNow(policy, now);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-
-  // 2nd request for submission.
-  now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 2);
-
-  now = new Date(now.getTime() + 5000);
-  defineNow(policy, now);
-
-  // On second failure we should back off by more.
-  yield listener.lastDataRequest.onSubmissionFailureSoft();
-  do_check_eq(policy.currentDaySubmissionFailureCount, 2);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-              new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[1]).getTime());
-
-  now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 3);
-
-  now = new Date(now.getTime() + 5000);
-  defineNow(policy, now);
-
-  // On 3rd failure we should back off by a whole day.
-  yield listener.lastDataRequest.onSubmissionFailureSoft();
-  do_check_eq(policy.currentDaySubmissionFailureCount, 0);
-  do_check_eq(policy.nextDataSubmissionDate.getTime(),
-              new Date(now.getTime() + 24 * 60 * 60 * 1000).getTime());
-});
-
-// Ensure that only one submission request can be active at a time.
-add_task(function* test_submission_expiring() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring");
-
-  let nextDataSubmission = policy.nextDataSubmissionDate;
-  let now = new Date(policy.nextDataSubmissionDate.getTime());
-  defineNow(policy, now);
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  defineNow(policy, new Date(now.getTime() + 500));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-
-  defineNow(policy, new Date(policy.now().getTime() +
-                             policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC));
-
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 2);
-});
-
-add_task(function* test_delete_remote_data() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data");
-
-  do_check_false(policy.pendingDeleteRemoteData);
-  let nextSubmissionDate = policy.nextDataSubmissionDate;
-
-  let now = new Date();
-  defineNow(policy, now);
-
-  policy.deleteRemoteData();
-  do_check_true(policy.pendingDeleteRemoteData);
-  do_check_neq(nextSubmissionDate.getTime(),
-               policy.nextDataSubmissionDate.getTime());
-  do_check_eq(now.getTime(), policy.nextDataSubmissionDate.getTime());
-
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-  do_check_true(listener.lastRemoteDeleteRequest.isDelete);
-  defineNow(policy, policy._futureDate(1000));
-
-  yield listener.lastRemoteDeleteRequest.onSubmissionSuccess(policy.now());
-  do_check_false(policy.pendingDeleteRemoteData);
-});
-
-// Ensure that deletion requests take priority over regular data submission.
-add_task(function* test_delete_remote_data_priority() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority");
-
-  let now = new Date();
-  defineNow(policy, new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000));
-
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  policy._inProgressSubmissionRequest = null;
-
-  policy.deleteRemoteData();
-  policy.checkStateAndTrigger();
-
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-  do_check_eq(listener.requestDataUploadCount, 1);
-});
-
-add_test(function test_delete_remote_data_backoff() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff");
-
-  let now = new Date();
-  defineNow(policy, now);
-  policy.nextDataSubmissionDate = now;
-  policy.deleteRemoteData();
-
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-  defineNow(policy, policy._futureDate(1000));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 0);
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-
-  defineNow(policy, policy._futureDate(500));
-  listener.lastRemoteDeleteRequest.onSubmissionFailureSoft();
-  defineNow(policy, policy._futureDate(50));
-
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-
-  defineNow(policy, policy._futureDate(policy.FAILURE_BACKOFF_INTERVALS[0] - 50));
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestRemoteDeleteCount, 2);
-
-  run_next_test();
-});
-
-// If we request delete while an upload is in progress, delete should be
-// scheduled immediately after upload.
-add_task(function* test_delete_remote_data_in_progress_upload() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload");
-
-  defineNow(policy, policy.nextDataSubmissionDate);
-
-  yield ensureUserNotifiedAndTrigger(policy);
-  do_check_eq(listener.requestDataUploadCount, 1);
-  defineNow(policy, policy._futureDate(50 * 1000));
-
-  // If we request a delete during a pending request, nothing should be done.
-  policy.deleteRemoteData();
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-  do_check_eq(listener.requestRemoteDeleteCount, 0);
-
-  // Now wait a little bit and finish the request.
-  defineNow(policy, policy._futureDate(10 * 1000));
-  yield listener.lastDataRequest.onSubmissionSuccess(policy._futureDate(1000));
-  defineNow(policy, policy._futureDate(5000));
-
-  policy.checkStateAndTrigger();
-  do_check_eq(listener.requestDataUploadCount, 1);
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-});
-
-add_test(function test_polling() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling");
-  let intended = 500;
-  let acceptable = 250;     // Because nsITimer doesn't guarantee times.
-
-  // Ensure checkStateAndTrigger is called at a regular interval.
-  let then = Date.now();
-  print("Starting run: " + then);
-  Object.defineProperty(policy, "POLL_INTERVAL_MSEC", {
-    value: intended,
-  });
-  let count = 0;
-
-  Object.defineProperty(policy, "checkStateAndTrigger", {
-    value: function fakeCheckStateAndTrigger() {
-      let now = Date.now();
-      let after = now - then;
-      count++;
-
-      print("Polled at " + now + " after " + after + "ms, intended " + intended);
-      do_check_true(after >= acceptable);
-      DataReportingPolicy.prototype.checkStateAndTrigger.call(policy);
-
-      if (count >= 2) {
-        policy.stopPolling();
-
-        do_check_eq(listener.requestDataUploadCount, 0);
-
-        run_next_test();
-      }
-
-      // "Specified timer period will be at least the time between when
-      // processing for last firing the callback completes and when the next
-      // firing occurs."
-      //
-      // That means we should set 'then' at the *end* of our handler, not
-      // earlier.
-      then = Date.now();
-    }
-  });
-  policy.startPolling();
-});
-
-add_task(function* test_record_health_report_upload_enabled() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled");
-
-  // Preconditions.
-  do_check_false(policy.pendingDeleteRemoteData);
-  do_check_true(policy.healthReportUploadEnabled);
-  do_check_eq(listener.requestRemoteDeleteCount, 0);
-
-  // User intent to disable should immediately result in a pending
-  // delete request.
-  policy.recordHealthReportUploadEnabled(false, "testing 1 2 3");
-  do_check_false(policy.healthReportUploadEnabled);
-  do_check_true(policy.pendingDeleteRemoteData);
-  do_check_eq(listener.requestRemoteDeleteCount, 1);
-
-  // Fulfilling it should make it go away.
-  yield listener.lastRemoteDeleteRequest.onNoDataAvailable();
-  do_check_false(policy.pendingDeleteRemoteData);
-
-  // User intent to enable should get us back to default state.
-  policy.recordHealthReportUploadEnabled(true, "testing 1 2 3");
-  do_check_false(policy.pendingDeleteRemoteData);
-  do_check_true(policy.healthReportUploadEnabled);
-});
-
-add_test(function test_pref_change_initiates_deletion() {
-  let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled");
-
-  // Preconditions.
-  do_check_false(policy.pendingDeleteRemoteData);
-  do_check_true(policy.healthReportUploadEnabled);
-  do_check_eq(listener.requestRemoteDeleteCount, 0);
-
-  // User intent to disable should indirectly result in a pending
-  // delete request, because the policy is watching for the pref
-  // to change.
-  Object.defineProperty(policy, "deleteRemoteData", {
-    value: function deleteRemoteDataProxy() {
-      do_check_false(policy.healthReportUploadEnabled);
-      do_check_false(policy.pendingDeleteRemoteData);     // Just called.
-
-      run_next_test();
-    },
-  });
-
-  hrPrefs.set("uploadEnabled", false);
-});
-
-add_task(function* test_policy_version() {
-  let policy, policyPrefs, hrPrefs, listener, now, firstRunTime;
-  function createPolicy(shouldBeNotified = false,
-                        currentPolicyVersion = 1, minimumPolicyVersion = 1,
-                        branchMinimumVersionOverride) {
-    [policy, policyPrefs, hrPrefs, listener] =
-      getPolicy("policy_version_test", currentPolicyVersion,
-                minimumPolicyVersion, branchMinimumVersionOverride);
-    let firstRun = now === undefined;
-    if (firstRun) {
-      firstRunTime = policy.firstRunDate.getTime();
-      do_check_true(firstRunTime > 0);
-      now = new Date(policy.firstRunDate.getTime());
-    }
-    else {
-      // The first-run time should not be reset even after policy-version
-      // upgrades.
-      do_check_eq(policy.firstRunDate.getTime(), firstRunTime);
-    }
-    defineNow(policy, now);
-    do_check_eq(policy.userNotifiedOfCurrentPolicy, shouldBeNotified);
-  }
-
-  function* triggerPolicyCheckAndEnsureNotified(notified = true) {
-    policy.checkStateAndTrigger();
-    do_check_eq(listener.notifyUserCount, Number(notified));
-    if (notified) {
-      policy.ensureUserNotified();
-      yield listener.lastNotifyRequest.deferred.promise;
-      do_check_true(policy.userNotifiedOfCurrentPolicy);
-      do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
-                  policyPrefs.get("currentPolicyVersion"));
-    }
-  }
-
-  createPolicy();
-  yield triggerPolicyCheckAndEnsureNotified();
-
-  // We shouldn't be notified again if the current version is still valid;
-  createPolicy(true);
-  yield triggerPolicyCheckAndEnsureNotified(false);
-
-  // Just increasing the current version isn't enough. The minimum
-  // version must be changed.
-  let currentPolicyVersion = policyPrefs.get("currentPolicyVersion");
-  let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion");
-  createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion);
-  yield triggerPolicyCheckAndEnsureNotified(true);
-  do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), currentPolicyVersion);
-
-  // Increase the minimum policy version and check if we're notified.
-
-  createPolicy(true, currentPolicyVersion, ++minimumPolicyVersion);
-  do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
-  yield triggerPolicyCheckAndEnsureNotified(false);
-
-
-  // Test increasing the minimum version just on the current channel.
-  createPolicy(true, currentPolicyVersion, minimumPolicyVersion);
-  yield triggerPolicyCheckAndEnsureNotified(false);
-  createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion, minimumPolicyVersion + 1);
-  yield triggerPolicyCheckAndEnsureNotified(true);
-});
deleted file mode 100644
--- a/services/datareporting/tests/xpcshell/xpcshell.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[DEFAULT]
-head = head.js
-tail =
-skip-if = toolkit == 'android' || toolkit == 'gonk'
-
-[test_policy.js]
deleted file mode 100644
--- a/services/docs/datareporting.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. _data_reporting_service:
-
-======================
-Data Reporting Service
-======================
-
-``/services/datareporting`` contains files related to an XPCOM service
-that collects and reports data within Gecko applications.
-
-The important files in this directory are:
-
-DataReportingService.js
-   An XPCOM service that coordinates collection and reporting of data.
-
-policy.jsm
-   A module containing the logic for coordinating and driving collection
-   and upload of data.
-
-sessions.jsm
-   Records Gecko application session history. This is loaded as part of
-   the XPCOM service because it needs to capture state from very early in
-   the application lifecycle. Bug 841561 tracks implementing this in C++.
-
-There is other code in the tree that collects and uploads data. The
-original intent of this directory and XPCOM service was to serve as a
-focal point for the coordination of all this activity so that it could
-all be done consistently and properly. This vision may or may not be fully
-realized.
--- a/services/docs/index.rst
+++ b/services/docs/index.rst
@@ -9,9 +9,8 @@ It was originally created to hold code f
 became the location for code written by the Mozilla Services Client team
 and thus includes :ref:`healthreport`. This team no longer exists, but
 the directory remains.
 
 .. toctree::
    :maxdepth: 1
 
    metrics
-   datareporting
--- a/services/moz.build
+++ b/services/moz.build
@@ -13,19 +13,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'andr
     DIRS += ['fxaccounts']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     # MOZ_SERVICES_HEALTHREPORT and therefore MOZ_DATA_REPORTING are
     # defined on Android, but these features are implemented using Java.
     if CONFIG['MOZ_SERVICES_HEALTHREPORT']:
         DIRS += ['healthreport']
 
-    if CONFIG['MOZ_DATA_REPORTING']:
-        DIRS += ['datareporting']
-
 if CONFIG['MOZ_SERVICES_METRICS']:
     DIRS += ['metrics']
 
 if CONFIG['MOZ_SERVICES_SYNC']:
     DIRS += ['sync']
 
 if CONFIG['MOZ_B2G'] or CONFIG['MOZ_B2GDROID']:
     DIRS += ['mobileid']
rename from services/datareporting/datareporting-prefs.js
rename to toolkit/components/telemetry/datareporting-prefs.js
--- a/services/datareporting/datareporting-prefs.js
+++ b/toolkit/components/telemetry/datareporting-prefs.js
@@ -1,13 +1,11 @@
 /* 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/. */
 
 pref("datareporting.policy.dataSubmissionEnabled", true);
-pref("datareporting.policy.dataSubmissionEnabled.v2", false);
-pref("datareporting.policy.firstRunTime", "0");
 pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
 pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0);
 pref("datareporting.policy.dataSubmissionPolicyBypassNotification", false);
 pref("datareporting.policy.currentPolicyVersion", 2);
 pref("datareporting.policy.minimumPolicyVersion", 1);
 pref("datareporting.policy.minimumPolicyVersion.channel-beta", 2);
--- a/toolkit/modules/SessionRecorder.jsm
+++ b/toolkit/modules/SessionRecorder.jsm
@@ -17,16 +17,19 @@ Cu.import("resource://services-common/ut
 
 // We automatically prune sessions older than this.
 const MAX_SESSION_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days.
 const STARTUP_RETRY_INTERVAL_MS = 5000;
 
 // Wait up to 5 minutes for startup measurements before giving up.
 const MAX_STARTUP_TRIES = 300000 / STARTUP_RETRY_INTERVAL_MS;
 
+const LOGGER_NAME = "Toolkit.Telemetry";
+const LOGGER_PREFIX = "SessionRecorder::";
+
 /**
  * Records information about browser sessions.
  *
  * This serves as an interface to both current session information as
  * well as a history of previous sessions.
  *
  * Typically only one instance of this will be installed in an
  * application. It is typically managed by an XPCOM service. The
@@ -59,17 +62,17 @@ this.SessionRecorder = function (branch)
   if (!branch) {
     throw new Error("branch argument must be defined.");
   }
 
   if (!branch.endsWith(".")) {
     throw new Error("branch argument must end with '.': " + branch);
   }
 
-  this._log = Log.repository.getLogger("Services.DataReporting.SessionRecorder");
+  this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
 
   this._prefs = new Preferences(branch);
   this._lastActivityWasInactive = false;
   this._activeTicks = 0;
   this.fineTotalTime = 0;
   this._started = false;
   this._timer = null;
   this._startupFieldTries = 0;