Bug 1346223 - Remove SessionRecorder.jsm. r?gfritzsche draft
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Mon, 24 Apr 2017 16:32:14 +0200
changeset 568672 5da02ef635f5b5eea9fbb7b5ab2e6ddf31e50c25
parent 567113 e17cbb839dd225a2da7e5d5bec43cf94e11749d8
child 626002 50edaf7e5c47bdfcca3bd8d37fb28834b2d61d0c
push id55953
push useralessio.placitelli@gmail.com
push dateWed, 26 Apr 2017 13:39:22 +0000
reviewersgfritzsche
bugs1346223
milestone55.0a1
Bug 1346223 - Remove SessionRecorder.jsm. r?gfritzsche This patch also moves the activeTicks logic to TelemetrySession.jsm along with the related test coverage. MozReview-Commit-ID: 8vXffqo2V85
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
toolkit/components/telemetry/tests/unit/test_TelemetrySession_activeTicks.js
toolkit/components/telemetry/tests/unit/xpcshell.ini
toolkit/modules/SessionRecorder.jsm
toolkit/modules/moz.build
toolkit/modules/tests/xpcshell/test_session_recorder.js
toolkit/modules/tests/xpcshell/xpcshell.ini
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -29,17 +29,16 @@ const LOGGER_PREFIX = "TelemetryControll
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_BRANCH_LOG = PREF_BRANCH + "log.";
 const PREF_SERVER = PREF_BRANCH + "server";
 const PREF_LOG_LEVEL = PREF_BRANCH_LOG + "level";
 const PREF_LOG_DUMP = PREF_BRANCH_LOG + "dump";
 const PREF_CACHED_CLIENTID = PREF_BRANCH + "cachedClientID";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
-const PREF_SESSIONS_BRANCH = "datareporting.sessions.";
 const PREF_UNIFIED = PREF_BRANCH + "unified";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
 const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false);
 
 const PING_FORMAT_VERSION = 4;
 
@@ -64,18 +63,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
-                                  "resource://gre/modules/SessionRecorder.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
                                   "resource://gre/modules/TelemetryArchive.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
                                   "resource://gre/modules/TelemetrySession.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
@@ -296,24 +293,16 @@ this.TelemetryController = Object.freeze
     options.addClientId = aOptions.addClientId || false;
     options.addEnvironment = aOptions.addEnvironment || false;
     options.overwrite = aOptions.overwrite || false;
 
     return Impl.savePing(aType, aPayload, aFilePath, options);
   },
 
   /**
-   * The session recorder instance managed by Telemetry.
-   * @return {Object} The active SessionRecorder instance or null if not available.
-   */
-  getSessionRecorder() {
-    return Impl._sessionRecorder;
-  },
-
-  /**
    * Allows waiting for TelemetryControllers delayed initialization to complete.
    * The returned promise is guaranteed to resolve before TelemetryController is shutting down.
    * @return {Promise} Resolved when delayed TelemetryController initialization completed.
    */
   promiseInitialized() {
     return Impl.promiseInitialized();
   },
 });
@@ -328,18 +317,16 @@ var Impl = {
   // Undefined if this is not the first run, or the previous build ID is unknown.
   _previousBuildID: undefined,
   _clientID: null,
   // A task performing delayed initialization
   _delayedInitTask: null,
   // The deferred promise resolved when the initialization task completes.
   _delayedInitTaskDeferred: null,
 
-  // The session recorder, shared with FHR and the Data Reporting Service.
-  _sessionRecorder: null,
   // This is a public barrier Telemetry clients can use to add blockers to the shutdown
   // of TelemetryController.
   // After this barrier, clients can not submit Telemetry pings anymore.
   _shutdownBarrier: new AsyncShutdown.Barrier("TelemetryController: Waiting for clients."),
   // This is a private barrier blocked by pending async ping activity (sending & saving).
   _connectionsBarrier: new AsyncShutdown.Barrier("TelemetryController: Waiting for pending ping activity"),
   // This is true when running in the test infrastructure.
   _testMode: false,
@@ -687,22 +674,16 @@ var Impl = {
     // This will trigger displaying the datachoices infobar.
     TelemetryReportingPolicy.setup();
 
     if (!this.enableTelemetryRecording()) {
       this._log.config("setupChromeProcess - Telemetry recording is disabled, skipping Chrome process setup.");
       return Promise.resolve();
     }
 
-    // Initialize the session recorder.
-    if (!this._sessionRecorder) {
-      this._sessionRecorder = new SessionRecorder(PREF_SESSIONS_BRANCH);
-      this._sessionRecorder.onStartup();
-    }
-
     this._attachObservers();
 
     // Perform a lightweight, early initialization for the component, just registering
     // a few observers and initializing the session.
     TelemetrySession.earlyInit(this._testMode);
 
     // Annotate crash reports so that we get pings for startup crashes
     TelemetrySend.earlyInit();
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -600,16 +600,18 @@ this.TelemetrySession = Object.freeze({
   testReset() {
     Impl._sessionId = null;
     Impl._subsessionId = null;
     Impl._previousSessionId = null;
     Impl._previousSubsessionId = null;
     Impl._subsessionCounter = 0;
     Impl._profileSubsessionCounter = 0;
     Impl._subsessionStartActiveTicks = 0;
+    Impl._sessionActiveTicks = 0;
+    Impl._isUserActive = true;
     Impl._subsessionStartTimeMonotonic = 0;
     Impl._lastEnvironmentChangeDate = Policy.monotonicNow();
     this.testUninstall();
   },
   /**
    * Triggers shutdown of the module.
    */
   shutdown() {
@@ -656,16 +658,20 @@ this.TelemetrySession = Object.freeze({
 var Impl = {
   _histograms: {},
   _initialized: false,
   _logger: null,
   _prevValues: {},
   _slowSQLStartup: {},
   _hasWindowRestoredObserver: false,
   _hasXulWindowVisibleObserver: false,
+  _hasActiveTicksObservers: false,
+  // The activity state for the user. If false, don't count the next
+  // active tick. Otherwise, increment the active ticks as usual.
+  _isUserActive: true,
   _startupIO: {},
   // The previous build ID, if this is the first run with a new build.
   // Null if this is the first run, or the previous build ID is unknown.
   _previousBuildId: null,
   // Telemetry payloads sent by child processes.
   // Each element is in the format {source: <weak-ref>, payload: <object>},
   // where source is a weak reference to the child process,
   // and payload is the telemetry payload from that child process.
@@ -697,16 +703,18 @@ var Impl = {
   _profileSubsessionCounter: 0,
   // Date of the last session split
   _subsessionStartDate: null,
   // Start time of the current subsession using a monotonic clock for the subsession
   // length measurements.
   _subsessionStartTimeMonotonic: 0,
   // The active ticks counted when the subsession starts
   _subsessionStartActiveTicks: 0,
+  // Active ticks in the whole session.
+  _sessionActiveTicks: 0,
   // A task performing delayed initialization of the chrome process
   _delayedInitTask: null,
   // Need a timeout in case children are tardy in giving back their memory reports.
   _totalMemoryTimeout: undefined,
   _testing: false,
   // An accumulator of total memory across all processes. Only valid once the final child reports.
   _totalMemory: null,
   // A Set of outstanding USS report ids
@@ -800,30 +808,26 @@ var Impl = {
     if (failedProfileLockCount)
       ret.failedProfileLockCount = failedProfileLockCount;
 
     for (let ioCounter in this._startupIO)
       ret[ioCounter] = this._startupIO[ioCounter];
 
     ret.savedPings = TelemetryStorage.pendingPingCount;
 
-    ret.activeTicks = -1;
-    let sr = TelemetryController.getSessionRecorder();
-    if (sr) {
-      let activeTicks = sr.activeTicks;
-      if (isSubsession) {
-        activeTicks = sr.activeTicks - this._subsessionStartActiveTicks;
-      }
+    let activeTicks = this._sessionActiveTicks;
+    if (isSubsession) {
+      activeTicks = this._sessionActiveTicks - this._subsessionStartActiveTicks;
+    }
 
-      if (clearSubsession) {
-        this._subsessionStartActiveTicks = activeTicks;
-      }
+    if (clearSubsession) {
+      this._subsessionStartActiveTicks = activeTicks;
+    }
 
-      ret.activeTicks = activeTicks;
-    }
+    ret.activeTicks = activeTicks;
 
     ret.pingsOverdue = TelemetrySend.overduePingsCount;
 
     return ret;
   },
 
   /**
    * When reflecting a histogram into JS, Telemetry hands us an object
@@ -1412,16 +1416,35 @@ var Impl = {
     let payload = this.getSessionPayload(reason, isSubsession);
     let options = {
       addClientId: true,
       addEnvironment: true,
     };
     return TelemetryController.submitExternalPing(getPingType(payload), payload, options);
   },
 
+  /**
+   * Attaches the needed observers during Telemetry early init, in the
+   * chrome process.
+   */
+  attachEarlyObservers() {
+    Services.obs.addObserver(this, "sessionstore-windows-restored");
+    if (AppConstants.platform === "android") {
+      Services.obs.addObserver(this, "application-background");
+    }
+    Services.obs.addObserver(this, "xul-window-visible");
+    this._hasWindowRestoredObserver = true;
+    this._hasXulWindowVisibleObserver = true;
+
+    // Attach the active-ticks related observers.
+    Services.obs.addObserver(this, "user-interaction-active");
+    Services.obs.addObserver(this, "user-interaction-inactive");
+    this._hasActiveTicksObservers = true;
+  },
+
   attachObservers: function attachObservers() {
     if (!this._initialized)
       return;
     Services.obs.addObserver(this, "idle-daily");
     if (Telemetry.canRecordExtended) {
       Services.obs.addObserver(this, TOPIC_CYCLE_COLLECTOR_BEGIN);
     }
   },
@@ -1477,23 +1500,17 @@ var Impl = {
     let previousBuildId = Preferences.get(PREF_PREVIOUS_BUILDID, null);
     let thisBuildID = Services.appinfo.appBuildID;
     // If there is no previousBuildId preference, we send null to the server.
     if (previousBuildId != thisBuildID) {
       this._previousBuildId = previousBuildId;
       Preferences.set(PREF_PREVIOUS_BUILDID, thisBuildID);
     }
 
-    Services.obs.addObserver(this, "sessionstore-windows-restored");
-    if (AppConstants.platform === "android") {
-      Services.obs.addObserver(this, "application-background");
-    }
-    Services.obs.addObserver(this, "xul-window-visible");
-    this._hasWindowRestoredObserver = true;
-    this._hasXulWindowVisibleObserver = true;
+    this.attachEarlyObservers();
 
     ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
   },
 
   /**
    * Does the "heavy" Telemetry initialization later on, so we
@@ -1799,17 +1816,16 @@ var Impl = {
       p.push(TelemetryController.submitExternalPing(getPingType(payload), payload, options)
                                 .catch(e => this._log.error("saveShutdownPings - failed to submit saved-session ping", e)));
     }
 
     // Wait on pings to be saved.
     return Promise.all(p);
   },
 
-
   testSavePendingPing() {
     let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
     let options = {
       addClientId: true,
       addEnvironment: true,
       overwrite: true,
     };
     return TelemetryController.addPendingPing(getPingType(payload), payload, options);
@@ -1826,16 +1842,21 @@ var Impl = {
     }
     if (this._hasXulWindowVisibleObserver) {
       Services.obs.removeObserver(this, "xul-window-visible");
       this._hasXulWindowVisibleObserver = false;
     }
     if (AppConstants.platform === "android") {
       Services.obs.removeObserver(this, "application-background");
     }
+    if (this._hasActiveTicksObservers) {
+      Services.obs.removeObserver(this, "user-interaction-active");
+      Services.obs.removeObserver(this, "user-interaction-inactive");
+      this._hasActiveTicksObservers = false;
+    }
     GCTelemetry.shutdown();
   },
 
   getPayload: function getPayload(reason, clearSubsession) {
     this._log.trace("getPayload - clearSubsession: " + clearSubsession);
     reason = reason || REASON_GATHER_PAYLOAD;
     // This function returns the current Telemetry payload to the caller.
     // We only gather startup info once.
@@ -1904,16 +1925,30 @@ var Impl = {
     this._addons = aAddOns;
   },
 
   testPing: function testPing() {
     return this.send(REASON_TEST_PING);
   },
 
   /**
+   * Tracks the number of "ticks" the user was active in.
+   */
+  _handleActiveTicks(aUserActive) {
+    const needsUpdate = aUserActive && this._isUserActive;
+    this._isUserActive = aUserActive;
+
+    // Don't count the first active tick after we get out of
+    // inactivity, because it is just the start of this active tick.
+    if (needsUpdate) {
+      this._sessionActiveTicks++;
+    }
+  },
+
+  /**
    * This observer drives telemetry.
    */
   observe(aSubject, aTopic, aData) {
     // Prevent the cycle collector begin topic from cluttering the log.
     if (aTopic != TOPIC_CYCLE_COLLECTOR_BEGIN) {
       this._log.trace("observe - " + aTopic + " notified.");
     }
 
@@ -1983,16 +2018,22 @@ var Impl = {
       let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
       let options = {
         addClientId: true,
         addEnvironment: true,
         overwrite: true,
       };
       TelemetryController.addPendingPing(getPingType(payload), payload, options);
       break;
+    case "user-interaction-active":
+      this._handleActiveTicks(true);
+      break;
+    case "user-interaction-inactive":
+      this._handleActiveTicks(false);
+      break;
     }
     return undefined;
   },
 
   /**
    * This tells TelemetrySession to uninitialize and save any pending pings.
    */
   shutdownChromeProcess() {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -927,39 +927,39 @@ add_task(function* test_checkSubsessionH
 
 add_task(function* test_checkSubsessionData() {
   if (gIsAndroid) {
     // We don't support subsessions yet on Android.
     return;
   }
 
   // Keep track of the active ticks count if the session recorder is available.
-  let sessionRecorder = TelemetryController.getSessionRecorder();
-  let activeTicksAtSubsessionStart = sessionRecorder.activeTicks;
+  let getActiveTicks = () => TelemetrySession.getPayload().simpleMeasurements.activeTicks;
+  let activeTicksAtSubsessionStart = getActiveTicks();
   let expectedActiveTicks = activeTicksAtSubsessionStart;
 
   let incrementActiveTicks = () => {
-    sessionRecorder.incrementActiveTicks();
+    TelemetrySession.observe(null, "user-interaction-active");
     ++expectedActiveTicks;
   }
 
   yield TelemetryController.testReset();
 
   // Both classic and subsession payload data should be the same on the first subsession.
   incrementActiveTicks();
   let classic = TelemetrySession.getPayload();
   let subsession = TelemetrySession.getPayload("environment-change");
   Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Classic pings must count active ticks since the beginning of the session.");
   Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Subsessions must count active ticks as classic pings on the first subsession.");
 
   // Start a new subsession and check that the active ticks are correctly reported.
   incrementActiveTicks();
-  activeTicksAtSubsessionStart = sessionRecorder.activeTicks;
+  activeTicksAtSubsessionStart = getActiveTicks();
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change", true);
   Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Classic pings must count active ticks since the beginning of the session.");
   Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Pings must not loose the tick count when starting a new subsession.");
 
   // Get a new subsession payload without clearing the subsession.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession_activeTicks.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+Cu.import("resource://gre/modules/TelemetryController.jsm", this);
+Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
+
+
+add_task(function* test_setup() {
+  // Addon manager needs a profile directory
+  do_get_profile();
+  loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  // Make sure we don't generate unexpected pings due to pref changes.
+  yield setEmptyPrefWatchlist();
+
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+});
+
+add_task(function* test_record_activeTicks() {
+  yield TelemetryController.testSetup();
+
+  let checkActiveTicks = (expected) => {
+    let payload = TelemetrySession.getPayload();
+    Assert.equal(payload.simpleMeasurements.activeTicks, expected,
+                 "TelemetrySession must record the expected number of active ticks.");
+  };
+
+  for (let i = 0; i < 3; i++) {
+    Services.obs.notifyObservers(null, "user-interaction-active");
+  }
+  checkActiveTicks(3);
+
+  // Now send inactive. This must not increment the active ticks.
+  Services.obs.notifyObservers(null, "user-interaction-inactive");
+  checkActiveTicks(3);
+
+  // If we send active again, this should be counted as inactive.
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(3);
+
+  // If we send active again, this should be counted as active.
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(4);
+
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(5);
+
+  yield TelemetryController.testShutdown();
+});
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -48,16 +48,17 @@ tags = addons
 tags = addons
 [test_TelemetryStopwatch.js]
 [test_TelemetryControllerBuildID.js]
 [test_TelemetrySendOldPings.js]
 skip-if = os == "android" # Disabled due to intermittent orange on Android
 tags = addons
 [test_TelemetrySession.js]
 tags = addons
+[test_TelemetrySession_activeTicks.js]
 [test_ThreadHangStats.js]
 run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
 [test_TelemetrySend.js]
 [test_ChildHistograms.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 tags = addons
 [test_ChildScalars.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
deleted file mode 100644
--- a/toolkit/modules/SessionRecorder.jsm
+++ /dev/null
@@ -1,403 +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 = [
-  "SessionRecorder",
-];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-common/utils.js");
-
-// 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
- * instance is instantiated at application start; onStartup is called
- * once the profile is installed; onShutdown is called during shutdown.
- *
- * We currently record state in preferences. However, this should be
- * invisible to external consumers. We could easily swap in a different
- * storage mechanism if desired.
- *
- * Please note the different semantics for storing times and dates in
- * preferences. Full dates (notably the session start time) are stored
- * as strings because preferences have a 32-bit limit on integer values
- * and milliseconds since UNIX epoch would overflow. Many times are
- * stored as integer offsets from the session start time because they
- * should not overflow 32 bits.
- *
- * Since this records history of all sessions, there is a possibility
- * for unbounded data aggregation. This is curtailed through:
- *
- *   1) An "idle-daily" observer which delete sessions older than
- *      MAX_SESSION_AGE_MS.
- *   2) The creator of this instance explicitly calling
- *      `pruneOldSessions`.
- *
- * @param branch
- *        (string) Preferences branch on which to record state.
- */
-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.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;
-
-  this._os = Cc["@mozilla.org/observer-service;1"]
-               .getService(Ci.nsIObserverService);
-
-};
-
-SessionRecorder.prototype = Object.freeze({
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  STARTUP_RETRY_INTERVAL_MS,
-
-  get _currentIndex() {
-    return this._prefs.get("currentIndex", 0);
-  },
-
-  set _currentIndex(value) {
-    this._prefs.set("currentIndex", value);
-  },
-
-  get _prunedIndex() {
-    return this._prefs.get("prunedIndex", 0);
-  },
-
-  set _prunedIndex(value) {
-    this._prefs.set("prunedIndex", value);
-  },
-
-  get startDate() {
-    return CommonUtils.getDatePref(this._prefs, "current.startTime");
-  },
-
-  set _startDate(value) {
-    CommonUtils.setDatePref(this._prefs, "current.startTime", value);
-  },
-
-  get activeTicks() {
-    return this._prefs.get("current.activeTicks", 0);
-  },
-
-  incrementActiveTicks() {
-    this._prefs.set("current.activeTicks", ++this._activeTicks);
-  },
-
-  /**
-   * Total time of this session in integer seconds.
-   *
-   * See also fineTotalTime for the time in milliseconds.
-   */
-  get totalTime() {
-    return this._prefs.get("current.totalTime", 0);
-  },
-
-  updateTotalTime() {
-    // We store millisecond precision internally to prevent drift from
-    // repeated rounding.
-    this.fineTotalTime = Date.now() - this.startDate;
-    this._prefs.set("current.totalTime", Math.floor(this.fineTotalTime / 1000));
-  },
-
-  get main() {
-    return this._prefs.get("current.main", -1);
-  },
-
-  set _main(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("main time must be an integer.");
-    }
-
-    this._prefs.set("current.main", value);
-  },
-
-  get firstPaint() {
-    return this._prefs.get("current.firstPaint", -1);
-  },
-
-  set _firstPaint(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("firstPaint must be an integer.");
-    }
-
-    this._prefs.set("current.firstPaint", value);
-  },
-
-  get sessionRestored() {
-    return this._prefs.get("current.sessionRestored", -1);
-  },
-
-  set _sessionRestored(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("sessionRestored must be an integer.");
-    }
-
-    this._prefs.set("current.sessionRestored", value);
-  },
-
-  getPreviousSessions() {
-    let result = {};
-
-    for (let i = this._prunedIndex; i < this._currentIndex; i++) {
-      let s = this.getPreviousSession(i);
-      if (!s) {
-        continue;
-      }
-
-      result[i] = s;
-    }
-
-    return result;
-  },
-
-  getPreviousSession(index) {
-    return this._deserialize(this._prefs.get("previous." + index));
-  },
-
-  /**
-   * Prunes old, completed sessions that started earlier than the
-   * specified date.
-   */
-  pruneOldSessions(date) {
-    for (let i = this._prunedIndex; i < this._currentIndex; i++) {
-      let s = this.getPreviousSession(i);
-      if (!s) {
-        continue;
-      }
-
-      if (s.startDate >= date) {
-        continue;
-      }
-
-      this._log.debug("Pruning session #" + i + ".");
-      this._prefs.reset("previous." + i);
-      this._prunedIndex = i;
-    }
-  },
-
-  recordStartupFields() {
-    let si = this._getStartupInfo();
-
-    if (!si.process) {
-      throw new Error("Startup info not available.");
-    }
-
-    let missing = false;
-
-    for (let field of ["main", "firstPaint", "sessionRestored"]) {
-      if (!(field in si)) {
-        this._log.debug("Missing startup field: " + field);
-        missing = true;
-        continue;
-      }
-
-      this["_" + field] = si[field].getTime() - si.process.getTime();
-    }
-
-    if (!missing || this._startupFieldTries > MAX_STARTUP_TRIES) {
-      this._clearStartupTimer();
-      return;
-    }
-
-    // If we have missing fields, install a timer and keep waiting for
-    // data.
-    this._startupFieldTries++;
-
-    if (!this._timer) {
-      this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      this._timer.initWithCallback({
-        notify: this.recordStartupFields.bind(this),
-      }, this.STARTUP_RETRY_INTERVAL_MS, this._timer.TYPE_REPEATING_SLACK);
-    }
-  },
-
-  _clearStartupTimer() {
-    if (this._timer) {
-      this._timer.cancel();
-      delete this._timer;
-    }
-  },
-
-  /**
-   * Perform functionality on application startup.
-   *
-   * This is typically called in a "profile-do-change" handler.
-   */
-  onStartup() {
-    if (this._started) {
-      throw new Error("onStartup has already been called.");
-    }
-
-    let si = this._getStartupInfo();
-    if (!si.process) {
-      throw new Error("Process information not available. Misconfigured app?");
-    }
-
-    this._started = true;
-
-    this._os.addObserver(this, "profile-before-change");
-    this._os.addObserver(this, "user-interaction-active");
-    this._os.addObserver(this, "user-interaction-inactive");
-    this._os.addObserver(this, "idle-daily");
-
-    // This has the side-effect of clearing current session state.
-    this._moveCurrentToPrevious();
-
-    this._startDate = si.process;
-    this._prefs.set("current.activeTicks", 0);
-    this.updateTotalTime();
-
-    this.recordStartupFields();
-  },
-
-  /**
-   * Record application activity.
-   */
-  onActivity(active) {
-    let updateActive = active && !this._lastActivityWasInactive;
-    this._lastActivityWasInactive = !active;
-
-    this.updateTotalTime();
-
-    if (updateActive) {
-      this.incrementActiveTicks();
-    }
-  },
-
-  onShutdown() {
-    this._log.info("Recording clean session shutdown.");
-    this._prefs.set("current.clean", true);
-    this.updateTotalTime();
-    this._clearStartupTimer();
-
-    this._os.removeObserver(this, "profile-before-change");
-    this._os.removeObserver(this, "user-interaction-active");
-    this._os.removeObserver(this, "user-interaction-inactive");
-    this._os.removeObserver(this, "idle-daily");
-  },
-
-  _CURRENT_PREFS: [
-    "current.startTime",
-    "current.activeTicks",
-    "current.totalTime",
-    "current.main",
-    "current.firstPaint",
-    "current.sessionRestored",
-    "current.clean",
-  ],
-
-  // This is meant to be called only during onStartup().
-  _moveCurrentToPrevious() {
-    try {
-      if (!this.startDate.getTime()) {
-        this._log.info("No previous session. Is this first app run?");
-        return;
-      }
-
-      let clean = this._prefs.get("current.clean", false);
-
-      let count = this._currentIndex++;
-      let obj = {
-        s: this.startDate.getTime(),
-        a: this.activeTicks,
-        t: this.totalTime,
-        c: clean,
-        m: this.main,
-        fp: this.firstPaint,
-        sr: this.sessionRestored,
-      };
-
-      this._log.debug("Recording last sessions as #" + count + ".");
-      this._prefs.set("previous." + count, JSON.stringify(obj));
-    } catch (ex) {
-      this._log.warn("Exception when migrating last session", ex);
-    } finally {
-      this._log.debug("Resetting prefs from last session.");
-      for (let pref of this._CURRENT_PREFS) {
-        this._prefs.reset(pref);
-      }
-    }
-  },
-
-  _deserialize(s) {
-    let o;
-    try {
-      o = JSON.parse(s);
-    } catch (ex) {
-      return null;
-    }
-
-    return {
-      startDate: new Date(o.s),
-      activeTicks: o.a,
-      totalTime: o.t,
-      clean: !!o.c,
-      main: o.m,
-      firstPaint: o.fp,
-      sessionRestored: o.sr,
-    };
-  },
-
-  // Implemented as a function to allow for monkeypatching in tests.
-  _getStartupInfo() {
-    return Cc["@mozilla.org/toolkit/app-startup;1"]
-             .getService(Ci.nsIAppStartup)
-             .getStartupInfo();
-  },
-
-  observe(subject, topic, data) {
-    switch (topic) {
-      case "profile-before-change":
-        this.onShutdown();
-        break;
-
-      case "user-interaction-active":
-        this.onActivity(true);
-        break;
-
-      case "user-interaction-inactive":
-        this.onActivity(false);
-        break;
-
-      case "idle-daily":
-        this.pruneOldSessions(new Date(Date.now() - MAX_SESSION_AGE_MS));
-        break;
-    }
-  },
-});
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -44,19 +44,16 @@ with Files('tests/xpcshell/test_NewTabUt
     BUG_COMPONENT = ('Firefox', 'New Tab Page')
 
 with Files('tests/xpcshell/test_UpdateUtils*.js'):
     BUG_COMPONENT = ('Toolkit', 'General')
 
 with Files('tests/xpcshell/test_client_id.js'):
     BUG_COMPONENT = ('Toolkit', 'Telemetry')
 
-with Files('tests/xpcshell/test_session_recorder.js'):
-    BUG_COMPONENT = ('Toolkit', 'Telemetry')
-
 with Files('AsyncPrefs.jsm'):
     BUG_COMPONENT = ('Core', 'Security: Process Sandboxing')
 
 with Files('CharsetMenu.jsm'):
     BUG_COMPONENT = ('Firefox', 'Toolbars and Customization')
 
 with Files('ClientID.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Telemetry')
@@ -137,19 +134,16 @@ with Files('RemoteSecurityUI.jsm'):
     BUG_COMPONENT = ('Firefox', 'Tabbed Browser')
 
 with Files('RemoteWebProgress.jsm'):
     BUG_COMPONENT = ('Core', 'Document Navigation')
 
 with Files('ResponsivenessMonitor.jsm'):
     BUG_COMPONENT = ('Firefox', 'Migration')
 
-with Files('SessionRecorder.jsm'):
-    BUG_COMPONENT = ('Toolkit', 'Telemetry')
-
 with Files('ShortcutUtils.jsm'):
     BUG_COMPONENT = ('Firefox', 'Toolbars and Customization')
 
 with Files('Sqlite.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Storage')
 
 with Files('UpdateUtils.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Add-ons Manager')
@@ -235,17 +229,16 @@ EXTRA_JS_MODULES += [
     'ResetProfile.jsm',
     'ResponsivenessMonitor.jsm',
     'secondscreen/RokuApp.jsm',
     'secondscreen/SimpleServiceDiscovery.jsm',
     'SelectContentHelper.jsm',
     'SelectParentHelper.jsm',
     'ServiceRequest.jsm',
     'Services.jsm',
-    'SessionRecorder.jsm',
     'sessionstore/FormData.jsm',
     'sessionstore/ScrollPosition.jsm',
     'sessionstore/XPathGenerator.jsm',
     'ShortcutUtils.jsm',
     'Sntp.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'Timer.jsm',
deleted file mode 100644
--- a/toolkit/modules/tests/xpcshell/test_session_recorder.js
+++ /dev/null
@@ -1,305 +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/Promise.jsm");
-Cu.import("resource://gre/modules/SessionRecorder.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://services-common/utils.js");
-
-
-function run_test() {
-  run_next_test();
-}
-
-function monkeypatchStartupInfo(recorder, start = new Date(), offset = 500) {
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: start,
-        main: new Date(start.getTime() + offset),
-        firstPaint: new Date(start.getTime() + 2 * offset),
-        sessionRestored: new Date(start.getTime() + 3 * offset),
-      };
-    }
-  });
-}
-
-function sleep(wait) {
-  let deferred = Promise.defer();
-
-  CommonUtils.namedTimer(function onTimer() {
-    deferred.resolve();
-  }, wait, deferred.promise, "_sleepTimer");
-
-  return deferred.promise;
-}
-
-function getRecorder(name, start, offset) {
-  let recorder = new SessionRecorder("testing." + name + ".");
-  monkeypatchStartupInfo(recorder, start, offset);
-
-  return recorder;
-}
-
-add_test(function test_basic() {
-  let recorder = getRecorder("basic");
-  recorder.onStartup();
-  recorder.onShutdown();
-
-  run_next_test();
-});
-
-add_task(function* test_current_properties() {
-  let now = new Date();
-  let recorder = getRecorder("current_properties", now);
-  yield sleep(25);
-  recorder.onStartup();
-
-  do_check_eq(recorder.startDate.getTime(), now.getTime());
-  do_check_eq(recorder.activeTicks, 0);
-  do_check_true(recorder.fineTotalTime > 0);
-  do_check_eq(recorder.main, 500);
-  do_check_eq(recorder.firstPaint, 1000);
-  do_check_eq(recorder.sessionRestored, 1500);
-
-  recorder.incrementActiveTicks();
-  do_check_eq(recorder.activeTicks, 1);
-
-  recorder._startDate = new Date(Date.now() - 1000);
-  recorder.updateTotalTime();
-  do_check_eq(recorder.totalTime, 1);
-
-  recorder.onShutdown();
-});
-
-// If startup info isn't present yet, we should install a timer and get
-// it eventually.
-add_task(function* test_current_availability() {
-  let recorder = new SessionRecorder("testing.current_availability.");
-  let now = new Date();
-
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: now,
-        main: new Date(now.getTime() + 500),
-        firstPaint: new Date(now.getTime() + 1000),
-      };
-    },
-    writable: true,
-  });
-
-  Object.defineProperty(recorder, "STARTUP_RETRY_INTERVAL_MS", {
-    value: 100,
-  });
-
-  let oldRecord = recorder.recordStartupFields;
-  let recordCount = 0;
-
-  Object.defineProperty(recorder, "recordStartupFields", {
-    value() {
-      recordCount++;
-      return oldRecord.call(recorder);
-    }
-  });
-
-  do_check_null(recorder._timer);
-  recorder.onStartup();
-  do_check_eq(recordCount, 1);
-  do_check_eq(recorder.sessionRestored, -1);
-  do_check_neq(recorder._timer, null);
-
-  yield sleep(125);
-  do_check_eq(recordCount, 2);
-  yield sleep(100);
-  do_check_eq(recordCount, 3);
-  do_check_eq(recorder.sessionRestored, -1);
-
-  monkeypatchStartupInfo(recorder, now);
-  yield sleep(100);
-  do_check_eq(recordCount, 4);
-  do_check_eq(recorder.sessionRestored, 1500);
-
-  // The timer should be removed and we should not fire again.
-  do_check_null(recorder._timer);
-  yield sleep(100);
-  do_check_eq(recordCount, 4);
-
-  recorder.onShutdown();
-});
-
-add_test(function test_timer_clear_on_shutdown() {
-  let recorder = new SessionRecorder("testing.timer_clear_on_shutdown.");
-  let now = new Date();
-
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: now,
-        main: new Date(now.getTime() + 500),
-        firstPaint: new Date(now.getTime() + 1000),
-      };
-    },
-  });
-
-  do_check_null(recorder._timer);
-  recorder.onStartup();
-  do_check_neq(recorder._timer, null);
-
-  recorder.onShutdown();
-  do_check_null(recorder._timer);
-
-  run_next_test();
-});
-
-add_task(function* test_previous_clean() {
-  let now = new Date();
-  let recorder = getRecorder("previous_clean", now);
-  yield sleep(25);
-  recorder.onStartup();
-
-  recorder.incrementActiveTicks();
-  recorder.incrementActiveTicks();
-
-  yield sleep(25);
-  recorder.onShutdown();
-
-  let total = recorder.totalTime;
-
-  yield sleep(25);
-  let now2 = new Date();
-  let recorder2 = getRecorder("previous_clean", now2, 100);
-  yield sleep(25);
-  recorder2.onStartup();
-
-  do_check_eq(recorder2.startDate.getTime(), now2.getTime());
-  do_check_eq(recorder2.main, 100);
-  do_check_eq(recorder2.firstPaint, 200);
-  do_check_eq(recorder2.sessionRestored, 300);
-
-  let sessions = recorder2.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 1);
-  do_check_true(0 in sessions);
-  let session = sessions[0];
-  do_check_true(session.clean);
-  do_check_eq(session.startDate.getTime(), now.getTime());
-  do_check_eq(session.main, 500);
-  do_check_eq(session.firstPaint, 1000);
-  do_check_eq(session.sessionRestored, 1500);
-  do_check_eq(session.totalTime, total);
-  do_check_eq(session.activeTicks, 2);
-
-  recorder2.onShutdown();
-});
-
-add_task(function* test_previous_abort() {
-  let now = new Date();
-  let recorder = getRecorder("previous_abort", now);
-  yield sleep(25);
-  recorder.onStartup();
-  recorder.incrementActiveTicks();
-  yield sleep(25);
-  let total = recorder.totalTime;
-  yield sleep(25);
-
-  let now2 = new Date();
-  let recorder2 = getRecorder("previous_abort", now2);
-  yield sleep(25);
-  recorder2.onStartup();
-
-  let sessions = recorder2.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 1);
-  do_check_true(0 in sessions);
-  let session = sessions[0];
-  do_check_false(session.clean);
-  do_check_eq(session.totalTime, total);
-
-  recorder.onShutdown();
-  recorder2.onShutdown();
-});
-
-add_task(function* test_multiple_sessions() {
-  for (let i = 0; i < 10; i++) {
-    let recorder = getRecorder("multiple_sessions");
-    yield sleep(25);
-    recorder.onStartup();
-    for (let j = 0; j < i; j++) {
-      recorder.incrementActiveTicks();
-    }
-    yield sleep(25);
-    recorder.onShutdown();
-    yield sleep(25);
-  }
-
-  let recorder = getRecorder("multiple_sessions");
-  recorder.onStartup();
-
-  let sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 10);
-
-  for (let [i, session] of Object.entries(sessions)) {
-    do_check_eq(session.activeTicks, i);
-
-    if (i > 0) {
-      do_check_true(session.startDate.getTime() > sessions[i - 1].startDate.getTime());
-    }
-  }
-
-  // #6 is preserved since >=.
-  let threshold = sessions[6].startDate;
-  recorder.pruneOldSessions(threshold);
-
-  sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 4);
-
-  recorder.pruneOldSessions(threshold);
-  sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 4);
-  do_check_eq(recorder._prunedIndex, 5);
-
-  recorder.onShutdown();
-});
-
-add_task(function* test_record_activity() {
-  let recorder = getRecorder("record_activity");
-  yield sleep(25);
-  recorder.onStartup();
-  let total = recorder.totalTime;
-  yield sleep(25);
-
-  for (let i = 0; i < 3; i++) {
-    Services.obs.notifyObservers(null, "user-interaction-active");
-    yield sleep(25);
-    do_check_true(recorder.fineTotalTime > total);
-    total = recorder.fineTotalTime;
-  }
-
-  do_check_eq(recorder.activeTicks, 3);
-
-  // Now send inactive. We should increment total time but not active.
-  Services.obs.notifyObservers(null, "user-interaction-inactive");
-  do_check_eq(recorder.activeTicks, 3);
-  do_check_true(recorder.fineTotalTime > total);
-  total = recorder.fineTotalTime;
-  yield sleep(25);
-
-  // If we send active again, this should be counted as inactive.
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 3);
-  do_check_true(recorder.fineTotalTime > total);
-  total = recorder.fineTotalTime;
-  yield sleep(25);
-
-  // If we send active again, this should be counted as active.
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 4);
-
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 5);
-
-  recorder.onShutdown();
-});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -49,18 +49,16 @@ skip-if = toolkit == 'android'
 [test_PromiseUtils.js]
 skip-if = toolkit == 'android'
 [test_propertyListsUtils.js]
 skip-if = toolkit == 'android'
 [test_readCertPrefs.js]
 skip-if = toolkit == 'android'
 [test_Services.js]
 skip-if = toolkit == 'android'
-[test_session_recorder.js]
-skip-if = toolkit == 'android'
 [test_sqlite.js]
 skip-if = toolkit == 'android'
 [test_sqlite_shutdown.js]
 skip-if = toolkit == 'android'
 [test_task.js]
 skip-if = toolkit == 'android'
 [test_timer.js]
 skip-if = toolkit == 'android'