Bug 1120372 - Introduce the "update" ping. r=bsmedberg,gfritzsche,mhowell

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

--HG--
extra : rebase_source : aa3ebda3cf1e1fc2cdb8c294a643973c5101fa1c
This commit is contained in:
Alessio Placitelli 2017-07-12 12:12:18 +02:00
parent 3823e46830
commit 2d82f2c5e6
11 changed files with 251 additions and 3 deletions

View File

@ -1516,6 +1516,8 @@ pref("toolkit.telemetry.archive.enabled", true);
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);

View File

@ -76,6 +76,8 @@ 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
@ -695,6 +697,10 @@ var Impl = {
// 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.
@ -781,6 +787,8 @@ var Impl = {
await this._delayedNewPingTask.finalize();
}
UpdatePing.shutdown();
// Stop the datachoices infobar display.
TelemetryReportingPolicy.shutdown();
TelemetryEnvironment.shutdown();

View File

@ -34,6 +34,7 @@ this.TelemetryUtils = {
ShutdownPingSender: "toolkit.telemetry.shutdownPingSender.enabled",
TelemetryEnabled: "toolkit.telemetry.enabled",
Unified: "toolkit.telemetry.unified",
UpdatePing: "toolkit.telemetry.updatePing.enabled",
NewProfilePingEnabled: "toolkit.telemetry.newProfilePing.enabled",
NewProfilePingDelay: "toolkit.telemetry.newProfilePing.delay",
PreviousBuildID: "toolkit.telemetry.previousBuildID",

View File

@ -0,0 +1,104 @@
/* -*- 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");
const LOGGER_NAME = "Toolkit.Telemetry";
const PING_TYPE = "update";
const UPDATE_DOWNLOADED_TOPIC = "update-downloaded";
this.EXPORTED_SYMBOLS = ["UpdatePing"];
/**
* 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 = [
"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 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) {
this._handleUpdateReady(aData);
}
},
shutdown() {
if (!this._enabled) {
return;
}
Services.obs.removeObserver(this, UPDATE_DOWNLOADED_TOPIC);
},
};

View File

@ -27,6 +27,6 @@ Important examples are:
* :doc:`main <../data/main-ping>` - contains the information collected by Telemetry (Histograms, hang stacks, ...)
* :doc:`saved-session <../data/main-ping>` - has the same format as a main ping, but it contains the *"classic"* Telemetry payload with measurements covering the whole browser session. This is only a separate type to make storage of saved-session easier server-side. This is temporary and will be removed soon.
* :doc:`crash <../data/crash-ping>` - a ping that is captured and sent after Firefox crashes.
* :doc:`new-profile <../data/new-profile-ping>` - sent on the first run of a new profile
* ``upgrade`` - *planned* - sent right after an upgrade
* :doc:`deletion <../data/deletion-ping>` - sent when FHR upload is disabled, requesting deletion of the data associated with this user
* :doc:`new-profile <../data/new-profile-ping>` - sent on the first run of a new profile.
* :doc:`update <../data/update-ping>` - sent right after an update is downloaded.
* :doc:`deletion <../data/deletion-ping>` - sent when FHR upload is disabled, requesting deletion of the data associated with this user.

View File

@ -0,0 +1,52 @@
"update" ping
==================
This opt-out ping is sent from Firefox Desktop when a browser update is ready to be applied. There is a
plan to send this ping after an update is successfully applied and the work will happen in `bug 1380256 <https://bugzilla.mozilla.org/show_bug.cgi?id=1380256>`_.
Structure:
.. code-block:: js
{
type: "update",
... common ping data
clientId: <UUID>,
environment: { ... },
payload: {
reason: <string>, // "ready"
targetChannel: <string>, // "nightly"
targetVersion: <string>, // "56.01a"
targetBuildId: <string>, // "20080811053724"
}
}
payload.reason
--------------
This field only supports the value ``ready``, meaning that the ping was generated after an update was downloaded
and marked as ready to be processed. For *non-staged* updates this happens as soon as the download
finishes and is verified while for *staged* updates this happens before the staging step is started.
payload.targetChannel
-----------------------
The Firefox channel the update was fetched from (only valid for pings with reason "ready").
payload.targetVersion
-----------------------
The Firefox version the browser is updating to. Follows the same format a application.version (only valid for pings with reason "ready").
payload.targetBuildId
-----------------------
The Firefox build id the browser is updating to. Follows the same format a application.buildId (only valid for pings with reason "ready").
Expected behaviours
-------------------
The following is a list of conditions and expected behaviours for the ``update`` ping:
- **The ping is generated once every time an update is downloaded, after it was verified:**
- *for users who saw the privacy policy*, the ``update`` ping is sent immediately;
- *for users who did not see the privacy policy*, the ``update`` ping is saved to disk and after the policy is displayed.
- **If the download of the update retries or other fallback occur**: the ``update`` ping will not be generated
multiple times, but only one time once the download is complete and verified.

View File

@ -62,6 +62,10 @@ Preferences
Controls the delay after which the :doc:`../data/new-profile` is sent on new profiles.
``toolkit.telemetry.updatePing.enabled``
Enable the :doc:`../data/update-ping` on browser updates.
Data-choices notification
-------------------------

View File

@ -95,6 +95,7 @@ EXTRA_JS_MODULES += [
'TelemetryUtils.jsm',
'ThirdPartyCookieProbe.jsm',
'UITelemetry.jsm',
'UpdatePing.jsm',
]
TESTING_JS_MODULES += [

View File

@ -5,6 +5,7 @@ support-files =
downloadPage.html
testConstants.js
[browser_TelemetryUpdatePing.js]
[browser_updatesBackgroundWindow.js]
[browser_updatesBackgroundWindowFailures.js]
[browser_updatesBasicPrompt.js]

View File

@ -0,0 +1,72 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
Cu.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);
/**
* Please note that this is really a Telemetry test, not an
* "update UI" test like the rest of the tests in this directory.
* This test does not live in toolkit/components/telemetry/tests to prevent
* duplicating the code for all the test dependencies. Unfortunately, due
* to a limitation in the build system, we were not able to simply reference
* the dependencies as "support-files" in the test manifest.
*/
add_task(async function testUpdatePingReady() {
SpecialPowers.pushPrefEnv({set: [
[PREF_APP_UPDATE_STAGING_ENABLED, false],
[PREF_APP_UPDATE_AUTO, false]
]});
let updateParams = "promptWaitTime=0";
let archiveChecker = new TelemetryArchiveTesting.Checker();
await archiveChecker.promiseInit();
// Trigger an "update" ping by downloading and applying an update.
await runUpdateTest(updateParams, 1, [
{
notificationId: "update-available",
button: "button",
beforeClick() {
checkWhatsNewLink("update-available-whats-new");
}
},
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
AppMenuNotifications.removeNotification(/.*/);
}
},
]);
// We cannot control when the ping will be generated/archived after we trigger
// an update, so let's make sure to have one before moving on with validation.
let updatePing;
await BrowserTestUtils.waitForCondition(async function() {
// Check that the ping made it into the Telemetry archive.
// The test data is defined in ../data/sharedUpdateXML.js
updatePing = await archiveChecker.promiseFindPing("update", [
[["payload", "reason"], "ready"],
[["payload", "targetBuildId"], "20080811053724"]
]);
return !!updatePing;
}, "Make sure the ping is generated before trying to validate it.", 500, 100);
ok(updatePing, "The 'update' ping must be correctly sent.");
// We don't know the exact value for the other fields, so just check
// that they're available.
for (let f of ["targetVersion", "targetChannel"]) {
ok(f in updatePing.payload,
`${f} must be available in the update ping payload.`);
ok(typeof(updatePing.payload[f]) == "string",
`${f} must have the correct format.`);
}
// Also make sure that the ping contains both a client id and an
// environment section.
ok("clientId" in updatePing, "The update ping must report a client id.");
ok("environment" in updatePing, "The update ping must report the environment.");
});

View File

@ -112,3 +112,6 @@ FINAL_TARGET_FILES += [
'TestAUSReadStrings2.ini',
'TestAUSReadStrings3.ini',
]
with Files("browser/browser_TelemetryUpdatePing.js"):
BUG_COMPONENT = ("Toolkit", "Telemetry")