diff options
Diffstat (limited to 'toolkit/components/telemetry/pings/UpdatePing.jsm')
-rw-r--r-- | toolkit/components/telemetry/pings/UpdatePing.jsm | 185 |
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); + }, +}; |