summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/pings/UpdatePing.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/pings/UpdatePing.jsm')
-rw-r--r--toolkit/components/telemetry/pings/UpdatePing.jsm185
1 files changed, 185 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/pings/UpdatePing.jsm b/toolkit/components/telemetry/pings/UpdatePing.jsm
new file mode 100644
index 0000000000..5cd4145bc4
--- /dev/null
+++ b/toolkit/components/telemetry/pings/UpdatePing.jsm
@@ -0,0 +1,185 @@
+/* -*- 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";
+
+ChromeUtils.import("resource://gre/modules/Log.jsm", this);
+ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TelemetryController",
+ "resource://gre/modules/TelemetryController.jsm"
+);
+
+const LOGGER_NAME = "Toolkit.Telemetry";
+const PING_TYPE = "update";
+const UPDATE_DOWNLOADED_TOPIC = "update-downloaded";
+const UPDATE_STAGED_TOPIC = "update-staged";
+
+var EXPORTED_SYMBOLS = ["UpdatePing"];
+
+/**
+ * This module is responsible for listening to all the relevant update
+ * signals, gathering the needed information and assembling the "update"
+ * ping.
+ */
+var UpdatePing = {
+ _enabled: false,
+
+ earlyInit() {
+ this._log = Log.repository.getLoggerWithMessagePrefix(
+ LOGGER_NAME,
+ "UpdatePing::"
+ );
+ this._enabled = Services.prefs.getBoolPref(
+ TelemetryUtils.Preferences.UpdatePing,
+ false
+ );
+
+ this._log.trace("init - enabled: " + this._enabled);
+
+ if (!this._enabled) {
+ return;
+ }
+
+ Services.obs.addObserver(this, UPDATE_DOWNLOADED_TOPIC);
+ Services.obs.addObserver(this, UPDATE_STAGED_TOPIC);
+ },
+
+ /**
+ * Get the information about the update we're going to apply/was just applied
+ * from the update manager.
+ *
+ * @return {nsIUpdate} The information about the update, if available, or null.
+ */
+ _getActiveUpdate() {
+ let updateManager = Cc["@mozilla.org/updates/update-manager;1"].getService(
+ Ci.nsIUpdateManager
+ );
+ if (!updateManager || !updateManager.readyUpdate) {
+ return null;
+ }
+
+ return updateManager.readyUpdate;
+ },
+
+ /**
+ * Generate an "update" ping with reason "success" and dispatch it
+ * to the Telemetry system.
+ *
+ * @param {String} aPreviousVersion The browser version we updated from.
+ * @param {String} aPreviousBuildId The browser build id we updated from.
+ */
+ handleUpdateSuccess(aPreviousVersion, aPreviousBuildId) {
+ if (!this._enabled) {
+ return;
+ }
+
+ this._log.trace("handleUpdateSuccess");
+
+ // An update could potentially change the update channel. Moreover,
+ // updates can only be applied if the update's channel matches with the build channel.
+ // There's no way to pass this information from the caller nor the environment as,
+ // in that case, the environment would report the "new" channel. However, the
+ // update manager should still have information about the active update: given the
+ // previous assumptions, we can simply get the channel from the update and assume
+ // it matches with the state previous to the update.
+ let update = this._getActiveUpdate();
+
+ const payload = {
+ reason: "success",
+ previousChannel: update ? update.channel : null,
+ previousVersion: aPreviousVersion,
+ previousBuildId: aPreviousBuildId,
+ };
+
+ const options = {
+ addClientId: true,
+ addEnvironment: true,
+ usePingSender: false,
+ };
+
+ TelemetryController.submitExternalPing(
+ PING_TYPE,
+ payload,
+ options
+ ).catch(e =>
+ this._log.error("handleUpdateSuccess - failed to submit update ping", e)
+ );
+ },
+
+ /**
+ * 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 = [
+ "applied",
+ "applied-service",
+ "pending",
+ "pending-service",
+ "pending-elevate",
+ ];
+ if (!ALLOWED_STATES.includes(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 update = this._getActiveUpdate();
+ if (!update) {
+ this._log.trace(
+ "Cannot get the update manager or no update is currently active."
+ );
+ return;
+ }
+
+ const payload = {
+ reason: "ready",
+ targetChannel: update.channel,
+ targetVersion: update.appVersion,
+ targetBuildId: update.buildID,
+ targetDisplayVersion: update.displayVersion,
+ };
+
+ 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 || aTopic == UPDATE_STAGED_TOPIC) {
+ this._handleUpdateReady(aData);
+ }
+ },
+
+ shutdown() {
+ if (!this._enabled) {
+ return;
+ }
+ Services.obs.removeObserver(this, UPDATE_DOWNLOADED_TOPIC);
+ Services.obs.removeObserver(this, UPDATE_STAGED_TOPIC);
+ },
+};