mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1743465: send additional information about existing installations in installation.first_seen Telemetry event. r=nalexander
This patch does two things: 1) Adds a few new pieces of information to the installation "first_seen" event. Specifically: * other_inst - true when any non-MSIX installation other than the running one was present when this event was prepared * other_msix_inst - true when any MSIX installation other that the running one was present when this event was prepared 2) Begins sending this event for MSIX installations Differential Revision: https://phabricator.services.mozilla.com/D134326
This commit is contained in:
parent
b9f8a703ea
commit
3e28c91b15
@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
SearchSERPTelemetry: "resource:///modules/SearchSERPTelemetry.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
WindowsInstallsInfo:
|
||||
"resource://gre/modules/components-utils/WindowsInstallsInfo.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
setInterval: "resource://gre/modules/Timer.jsm",
|
||||
clearTimeout: "resource://gre/modules/Timer.jsm",
|
||||
@ -1196,72 +1198,148 @@ let BrowserUsageTelemetry = {
|
||||
* if so then send installation telemetry.
|
||||
*
|
||||
* @param {nsIFile} [dataPathOverride] Optional, full data file path, for tests.
|
||||
* @param {Array<string>} [msixPackagePrefixes] Optional, list of prefixes to
|
||||
consider "existing" installs when looking at installed MSIX packages.
|
||||
Defaults to prefixes for builds produced in Firefox automation.
|
||||
* @return {Promise}
|
||||
* @resolves When the event has been recorded, or if the data file was not found.
|
||||
* @rejects JavaScript exception on any failure.
|
||||
*/
|
||||
async reportInstallationTelemetry(dataPathOverride) {
|
||||
async reportInstallationTelemetry(
|
||||
dataPathOverride,
|
||||
msixPackagePrefixes = ["Mozilla.Firefox", "Mozilla.MozillaFirefox"]
|
||||
) {
|
||||
if (AppConstants.platform != "win") {
|
||||
// This is a windows-only feature.
|
||||
return;
|
||||
}
|
||||
|
||||
let dataPath = dataPathOverride;
|
||||
if (!dataPath) {
|
||||
dataPath = Services.dirsvc.get("GreD", Ci.nsIFile);
|
||||
dataPath.append("installation_telemetry.json");
|
||||
}
|
||||
|
||||
let dataBytes;
|
||||
try {
|
||||
dataBytes = await IOUtils.read(dataPath.path);
|
||||
} catch (ex) {
|
||||
if (ex.name == "NotFoundError") {
|
||||
// Many systems will not have the data file, return silently if not found as
|
||||
// there is nothing to record.
|
||||
return;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
const dataString = new TextDecoder("utf-16").decode(dataBytes);
|
||||
const data = JSON.parse(dataString);
|
||||
|
||||
const TIMESTAMP_PREF = "app.installation.timestamp";
|
||||
const lastInstallTime = Services.prefs.getStringPref(TIMESTAMP_PREF, null);
|
||||
const wpm = Cc["@mozilla.org/windows-package-manager;1"].createInstance(
|
||||
Ci.nsIWindowsPackageManager
|
||||
);
|
||||
let installer_type = "";
|
||||
let pfn;
|
||||
try {
|
||||
pfn = Services.sysinfo.getProperty("winPackageFamilyName");
|
||||
} catch (e) {}
|
||||
|
||||
if (lastInstallTime && data.install_timestamp == lastInstallTime) {
|
||||
// We've already seen this install
|
||||
return;
|
||||
function getInstallData() {
|
||||
// We only care about where _any_ other install existed - no
|
||||
// need to count more than 1.
|
||||
const installPaths = WindowsInstallsInfo.getInstallPaths(
|
||||
1,
|
||||
new Set([Services.dirsvc.get("GreBinD", Ci.nsIFile).path])
|
||||
);
|
||||
const msixInstalls = new Set();
|
||||
// We're just going to eat all errors here -- we don't want the event
|
||||
// to go unsent if we were unable to look for MSIX installs.
|
||||
try {
|
||||
wpm
|
||||
.findUserInstalledPackages(msixPackagePrefixes)
|
||||
.forEach(i => msixInstalls.add(i));
|
||||
if (pfn) {
|
||||
msixInstalls.delete(pfn);
|
||||
}
|
||||
} catch (ex) {}
|
||||
return {
|
||||
installPaths,
|
||||
msixInstalls,
|
||||
};
|
||||
}
|
||||
|
||||
// First time seeing this install, record the timestamp.
|
||||
Services.prefs.setStringPref(TIMESTAMP_PREF, data.install_timestamp);
|
||||
let extra = {};
|
||||
|
||||
// Installation timestamp is not intended to be sent with telemetry,
|
||||
// remove it to emphasize this point.
|
||||
delete data.install_timestamp;
|
||||
if (pfn) {
|
||||
if (lastInstallTime != null) {
|
||||
// We've already seen this install
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the extra event data
|
||||
let extra = {
|
||||
version: data.version,
|
||||
build_id: data.build_id,
|
||||
admin_user: data.admin_user.toString(),
|
||||
install_existed: data.install_existed.toString(),
|
||||
profdir_existed: data.profdir_existed.toString(),
|
||||
};
|
||||
// First time seeing this install, record the timestamp.
|
||||
Services.prefs.setStringPref(TIMESTAMP_PREF, wpm.getInstalledDate());
|
||||
let install_data = getInstallData();
|
||||
|
||||
if (data.installer_type == "full") {
|
||||
extra.silent = data.silent.toString();
|
||||
extra.from_msi = data.from_msi.toString();
|
||||
extra.default_path = data.default_path.toString();
|
||||
installer_type = "msix";
|
||||
|
||||
// Build the extra event data
|
||||
extra.version = AppConstants.MOZ_APP_VERSION;
|
||||
extra.build_id = AppConstants.MOZ_BUILDID;
|
||||
// The next few keys are static for the reasons described
|
||||
// No way to detect whether or not we were installed by an admin
|
||||
extra.admin_user = "false";
|
||||
// Always false at the moment, because we create a new profile
|
||||
// on first launch
|
||||
extra.profdir_existed = "false";
|
||||
// Obviously false for MSIX installs
|
||||
extra.from_msi = "false";
|
||||
// We have no way of knowing whether we were installed via the GUI,
|
||||
// through the command line, or some Enterprise management tool.
|
||||
extra.silent = "false";
|
||||
// There's no way to change the install path for an MSIX package
|
||||
extra.default_path = "true";
|
||||
extra.install_existed = install_data.msixInstalls.has(pfn).toString();
|
||||
install_data.msixInstalls.delete(pfn);
|
||||
extra.other_inst = (!!install_data.installPaths.size).toString();
|
||||
extra.other_msix_inst = (!!install_data.msixInstalls.size).toString();
|
||||
} else {
|
||||
let dataPath = dataPathOverride;
|
||||
if (!dataPath) {
|
||||
dataPath = Services.dirsvc.get("GreD", Ci.nsIFile);
|
||||
dataPath.append("installation_telemetry.json");
|
||||
}
|
||||
|
||||
let dataBytes;
|
||||
try {
|
||||
dataBytes = await IOUtils.read(dataPath.path);
|
||||
} catch (ex) {
|
||||
if (ex.name == "NotFoundError") {
|
||||
// Many systems will not have the data file, return silently if not found as
|
||||
// there is nothing to record.
|
||||
return;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
const dataString = new TextDecoder("utf-16").decode(dataBytes);
|
||||
const data = JSON.parse(dataString);
|
||||
|
||||
if (lastInstallTime && data.install_timestamp == lastInstallTime) {
|
||||
// We've already seen this install
|
||||
return;
|
||||
}
|
||||
|
||||
// First time seeing this install, record the timestamp.
|
||||
Services.prefs.setStringPref(TIMESTAMP_PREF, data.install_timestamp);
|
||||
let install_data = getInstallData();
|
||||
|
||||
installer_type = data.installer_type;
|
||||
|
||||
// Installation timestamp is not intended to be sent with telemetry,
|
||||
// remove it to emphasize this point.
|
||||
delete data.install_timestamp;
|
||||
|
||||
// Build the extra event data
|
||||
extra.version = data.version;
|
||||
extra.build_id = data.build_id;
|
||||
extra.admin_user = data.admin_user.toString();
|
||||
extra.install_existed = data.install_existed.toString();
|
||||
extra.profdir_existed = data.profdir_existed.toString();
|
||||
extra.other_inst = (!!install_data.installPaths.size).toString();
|
||||
extra.other_msix_inst = (!!install_data.msixInstalls.size).toString();
|
||||
|
||||
if (data.installer_type == "full") {
|
||||
extra.silent = data.silent.toString();
|
||||
extra.from_msi = data.from_msi.toString();
|
||||
extra.default_path = data.default_path.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Record the event
|
||||
Services.telemetry.setEventRecordingEnabled("installation", true);
|
||||
Services.telemetry.recordEvent(
|
||||
"installation",
|
||||
"first_seen",
|
||||
data.installer_type,
|
||||
installer_type,
|
||||
null,
|
||||
extra
|
||||
);
|
||||
|
@ -3,6 +3,9 @@
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
const { BrowserUsageTelemetry } = ChromeUtils.import(
|
||||
"resource:///modules/BrowserUsageTelemetry.jsm"
|
||||
);
|
||||
@ -36,7 +39,7 @@ function writeJsonUtf16(fileName, obj) {
|
||||
async function runReport(
|
||||
dataFile,
|
||||
installType,
|
||||
{ clearTS, setTS, assertRejects, expectExtra, expectTS }
|
||||
{ clearTS, setTS, assertRejects, expectExtra, expectTS, msixPrefixes }
|
||||
) {
|
||||
// Setup timestamp
|
||||
if (clearTS) {
|
||||
@ -55,8 +58,13 @@ async function runReport(
|
||||
BrowserUsageTelemetry.reportInstallationTelemetry(dataFile),
|
||||
assertRejects
|
||||
);
|
||||
} else {
|
||||
} else if (!msixPrefixes) {
|
||||
await BrowserUsageTelemetry.reportInstallationTelemetry(dataFile);
|
||||
} else {
|
||||
await BrowserUsageTelemetry.reportInstallationTelemetry(
|
||||
dataFile,
|
||||
msixPrefixes
|
||||
);
|
||||
}
|
||||
|
||||
// Check events
|
||||
@ -105,6 +113,8 @@ add_task(async function testInstallationTelemetry() {
|
||||
build_id: "123",
|
||||
admin_user: "true",
|
||||
install_existed: "false",
|
||||
other_inst: "false",
|
||||
other_msix_inst: "false",
|
||||
profdir_existed: "false",
|
||||
};
|
||||
|
||||
@ -145,6 +155,8 @@ add_task(async function testInstallationTelemetry() {
|
||||
build_id: "123",
|
||||
admin_user: "false",
|
||||
install_existed: "true",
|
||||
other_inst: "false",
|
||||
other_msix_inst: "false",
|
||||
profdir_existed: "true",
|
||||
silent: "false",
|
||||
from_msi: "false",
|
||||
@ -161,12 +173,18 @@ add_task(async function testInstallationTelemetry() {
|
||||
// Check that it doesn't generate another event when the timestamp is unchanged
|
||||
await runReport(dataFile, "full", { expectTS: "1" });
|
||||
|
||||
// New timestamp
|
||||
// New timestamp and a check to make sure we can find installed MSIX packages
|
||||
// by overriding the prefixes a bit further down.
|
||||
fullData.install_timestamp = "2";
|
||||
// This check only works on Windows 10 and above
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
|
||||
fullExtra.other_msix_inst = "true";
|
||||
}
|
||||
await writeJsonUtf16(dataFilePath, fullData);
|
||||
await runReport(dataFile, "full", {
|
||||
expectExtra: fullExtra,
|
||||
expectTS: "2",
|
||||
msixPrefixes: ["Microsoft"],
|
||||
});
|
||||
|
||||
// Missing field
|
||||
@ -184,4 +202,7 @@ add_task(async function testInstallationTelemetry() {
|
||||
// Missing file, should return with no exception
|
||||
await IOUtils.remove(dataFilePath);
|
||||
await runReport(dataFile, "stub", { setTS: "3", expectTS: "3" });
|
||||
|
||||
// bug 1750581 tracks testing this when we're able to run tests in
|
||||
// an MSIX package environment
|
||||
});
|
||||
|
@ -2964,6 +2964,7 @@ installation:
|
||||
objects:
|
||||
- full # if the full installer was run directly
|
||||
- stub # if the stub installer was used
|
||||
- msix # if the installation was done through an MSIX package
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes: ["main"]
|
||||
products: ["firefox"]
|
||||
@ -2973,11 +2974,13 @@ installation:
|
||||
build_id: The build ID of the application installed by the installer (not necessarily the current version)
|
||||
admin_user: Whether the installer is running from an elevated admin user
|
||||
install_existed: Whether there was already an install in this location
|
||||
other_inst: Whether there was already any non-MSIX install on this system
|
||||
other_msix_inst: Whether there was already any MSIX install on this system
|
||||
profdir_existed: Whether the top-level profile directory existed
|
||||
silent: '(optional, present if object is "full") Whether this was a silent install'
|
||||
from_msi: '(optional, present if object is "full") Whether this was an MSI install'
|
||||
default_path: '(optional, present if object is "full") Whether the default path was used'
|
||||
bug_numbers: [1660198, 1725295]
|
||||
bug_numbers: [1660198, 1725295, 1743465]
|
||||
notification_emails:
|
||||
- application-update-telemetry-alerts@mozilla.com
|
||||
- rtestard@mozilla.com
|
||||
|
Loading…
Reference in New Issue
Block a user