Bug 1120372 - Introduce the "update" ping. r? draft
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Wed, 12 Jul 2017 12:12:18 +0200
changeset 607609 cff3ee1d0efa476c9e239c90022da83d6bd99758
parent 606958 6fec4855b5345eb63fef57089e61829b88f5f4eb
child 637081 db5f4d04783ae17066997c384ef916ebcdac5ae9
push id68039
push useralessio.placitelli@gmail.com
push dateWed, 12 Jul 2017 14:31:12 +0000
bugs1120372
milestone56.0a1
Bug 1120372 - Introduce the "update" ping. r? This ping will be sent as soon as an update is ready to be applied, after it's downloaded. This is currently enabled but protected behind the 'toolkit.telemetry.updatePing.enabled' preference. MozReview-Commit-ID: 4TuM0e5MzBl
browser/app/profile/firefox.js
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetryUtils.jsm
toolkit/components/telemetry/UpdatePing.jsm
toolkit/components/telemetry/moz.build
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1491,16 +1491,18 @@ pref("browser.translation.engine", "bing
 
 // Telemetry settings.
 // Determines if Telemetry pings can be archived locally.
 pref("toolkit.telemetry.archive.enabled", true);
 // Enables sending the shutdown ping when Firefox shuts down.
 pref("toolkit.telemetry.shutdownPingSender.enabled", true);
 // Enables sending the 'new-profile' ping on new profiles.
 pref("toolkit.telemetry.newProfilePing.enabled", true);
+// Enables sending 'update' pings on Firefox updates.
+pref("toolkit.telemetry.updatePing.enabled", true);
 
 // Telemetry experiments settings.
 pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 // Whether experiments are supported by the current application profile.
 pref("experiments.supported", true);
 
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -80,16 +80,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
                                   "resource://gre/modules/TelemetrySession.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy",
                                   "resource://gre/modules/TelemetryReportingPolicy.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryModules",
                                   "resource://gre/modules/TelemetryModules.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdatePing",
+                                  "resource://gre/modules/UpdatePing.jsm");
 
 /**
  * Setup Telemetry logging. This function also gets called when loggin related
  * preferences change.
  */
 var gLogger = null;
 var gLogAppenderDump = null;
 function configureLogging() {
@@ -705,16 +707,20 @@ var Impl = {
     TelemetrySend.earlyInit();
 
     // For very short session durations, we may never load the client
     // id from disk.
     // We try to cache it in prefs to avoid this, even though this may
     // lead to some stale client ids.
     this._clientID = ClientID.getCachedClientID();
 
+    // Init the update ping telemetry as early as possible. This won't have
+    // an impact on startup.
+    UpdatePing.earlyInit();
+
     // Delay full telemetry initialization to give the browser time to
     // run various late initializers. Otherwise our gathered memory
     // footprint and other numbers would be too optimistic.
     this._delayedInitTaskDeferred = PromiseUtils.defer();
     this._delayedInitTask = new DeferredTask(async () => {
       try {
         // TODO: This should probably happen after all the delayed init here.
         this._initialized = true;
@@ -791,16 +797,18 @@ var Impl = {
     this._detachObservers();
 
     // Now do an orderly shutdown.
     try {
       if (this._delayedNewPingTask) {
         await this._delayedNewPingTask.finalize();
       }
 
+      UpdatePing.shutdown();
+
       // Stop the datachoices infobar display.
       TelemetryReportingPolicy.shutdown();
       TelemetryEnvironment.shutdown();
 
       // Stop any ping sending.
       await TelemetrySend.shutdown();
 
       await TelemetrySession.shutdown();
--- a/toolkit/components/telemetry/TelemetryUtils.jsm
+++ b/toolkit/components/telemetry/TelemetryUtils.jsm
@@ -29,16 +29,17 @@ this.TelemetryUtils = {
     ArchiveEnabled: "toolkit.telemetry.archive.enabled",
     CachedClientId: "toolkit.telemetry.cachedClientID",
     FirstRun: "toolkit.telemetry.reportingpolicy.firstRun",
     OverrideOfficialCheck: "toolkit.telemetry.send.overrideOfficialCheck",
     Server: "toolkit.telemetry.server",
     ShutdownPingSender: "toolkit.telemetry.shutdownPingSender.enabled",
     TelemetryEnabled: "toolkit.telemetry.enabled",
     Unified: "toolkit.telemetry.unified",
+    UpdatePing: "toolkit.telemetry.updatePing.enabled",
 
     // Data reporting Preferences
     AcceptedPolicyDate: "datareporting.policy.dataSubmissionPolicyNotifiedTime",
     AcceptedPolicyVersion: "datareporting.policy.dataSubmissionPolicyAcceptedVersion",
     BypassNotification: "datareporting.policy.dataSubmissionPolicyBypassNotification",
     CurrentPolicyVersion: "datareporting.policy.currentPolicyVersion",
     DataSubmissionEnabled: "datareporting.policy.dataSubmissionEnabled",
     FhrUploadEnabled: "datareporting.healthreport.uploadEnabled",
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/UpdatePing.jsm
@@ -0,0 +1,105 @@
+/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+/* 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/Log.jsm", this);
+Cu.import("resource://gre/modules/Preferences.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
+                                  "resource://gre/modules/TelemetryController.jsm");
+
+// TODO: rename this UpdatesTelemetry.jsm?
+
+const LOGGER_NAME = "Toolkit.Telemetry";
+const PING_TYPE = "update";
+const UPDATE_DOWNLOADED_TOPIC = "update-downloaded";
+
+/**
+ * This module is responsible for listening to all the relevant update
+ * signals, gathering the needed information and assembling the "update"
+ * ping.
+ */
+this.UpdatePing = {
+  earlyInit() {
+    this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, "UpdatePing::");
+    this._enabled = Preferences.get(TelemetryUtils.Preferences.UpdatePing, false);
+
+    this._log.trace("init - enabled: " + this._enabled);
+
+    if (!this._enabled) {
+      return;
+    }
+
+    Services.obs.addObserver(this, UPDATE_DOWNLOADED_TOPIC);
+  },
+
+  /**
+   * Generate an "update" ping with reason "ready" and dispatch it
+   * to the Telemetry system.
+   *
+   * @param {String} aUpdateState The state of the downloaded patch. See
+   *        nsIUpdateService.idl for a list of possible values.
+   */
+  _handleUpdateReady(aUpdateState) {
+    const ALLOWED_STATES = [ "pending", "pending-service", "pending-elevate" ];
+    if (!ALLOWED_STATES.include(aUpdateState)) {
+      this._log.trace("Unexpected update state: " + aUpdateState);
+      return;
+    }
+
+    // Get the information about the update we're going to apply from the
+    // update manager.
+    let updateManager =
+      Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager);
+    if (!updateManager || !updateManager.activeUpdate) {
+      this._log.trace("Cannot get the update manager or no update is currently active.");
+      return;
+    }
+
+    let update = updateManager.activeUpdate;
+
+    const PAYLOAD = {
+      reason: "ready",
+      targetChannel: update.channel,
+      targetVersion: update.appVersion,
+      targetBuildId: update.buildID,
+    };
+
+    const OPTIONS = {
+      addClientId: true,
+      addEnvironment: true,
+      usePingSender: true,
+    };
+
+    TelemetryController.submitExternalPing(PING_TYPE, PAYLOAD, OPTIONS)
+                       .catch(e => this._log.error("_handleUpdateReady - failed to submit update ping", e));
+  },
+
+  /**
+   * The notifications handler.
+   */
+  observe(aSubject, aTopic, aData) {
+    this._log.trace("observe - aTopic: " + aTopic);
+    if (aTopic == UPDATE_DOWNLOADED_TOPIC) {
+      // TODO: Do we need to listen to staged too?
+      // Does this imply that the update is ready?
+      this._handleUpdateReady(aData);
+    }
+  },
+
+  shutdown() {
+    if (!this._enabled) {
+      return;
+    }
+
+    Services.obs.removeObserver(this, UPDATE_DOWNLOADED_TOPIC);
+  },
+};
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -90,16 +90,17 @@ EXTRA_JS_MODULES += [
     'TelemetrySend.jsm',
     'TelemetrySession.jsm',
     'TelemetryStopwatch.jsm',
     'TelemetryStorage.jsm',
     'TelemetryTimestamps.jsm',
     'TelemetryUtils.jsm',
     'ThirdPartyCookieProbe.jsm',
     'UITelemetry.jsm',
+    'UpdatePing.jsm',
 ]
 
 TESTING_JS_MODULES += [
   'tests/unit/TelemetryArchiveTesting.jsm',
 ]
 
 GENERATED_FILES = [
     'TelemetryEventData.h',