Bug 1697555 - Remove Telemetry portions of Accounts Ecosystem Telemetry r=Dexter

Differential Revision: https://phabricator.services.mozilla.com/D110091
This commit is contained in:
Chris H-C 2021-03-29 20:03:38 +00:00
parent 2aac69d13c
commit ea2a18d757
17 changed files with 71 additions and 1178 deletions

View File

@ -612,7 +612,6 @@ module.exports = {
"toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js",
"toolkit/components/processsingleton/MainProcessSingleton.jsm",
"toolkit/components/telemetry/tests/unit/head.js",
"toolkit/components/telemetry/tests/unit/test_EcosystemTelemetry.js",
"toolkit/components/telemetry/tests/unit/test_EventPing.js",
"toolkit/components/telemetry/tests/unit/test_HealthPing.js",
"toolkit/components/telemetry/tests/unit/test_PingAPI.js",

View File

@ -1681,8 +1681,6 @@ pref("toolkit.telemetry.newProfilePing.enabled", true);
pref("toolkit.telemetry.updatePing.enabled", true);
// Enables sending 'bhr' pings when the browser hangs.
pref("toolkit.telemetry.bhrPing.enabled", true);
// Whether to enable Ecosystem Telemetry, requires a restart.
pref("toolkit.telemetry.ecosystemtelemetry.enabled", false);
// Ping Centre Telemetry settings.
pref("browser.ping-centre.telemetry", true);

View File

@ -126,7 +126,7 @@ add_task(async function test_discovery() {
equal(cookie.host, uri.host, "cookie exists for host");
return true;
});
await ClientID.removeClientIDs();
await ClientID.removeClientID();
await ClientID.getClientID();
await changed;

View File

@ -68,7 +68,6 @@ class PingServer(Layer):
"toolkit.telemetry.enabled": True,
"toolkit.telemetry.unified": True,
"toolkit.telemetry.shutdownPingSender.enabled": True,
"toolkit.telemetry.ecosystemtelemetry.enabled": True,
"datareporting.policy.dataSubmissionPolicyBypassNotification": True,
"toolkit.telemetry.send.overrideOfficialCheck": True,
}

View File

@ -12,7 +12,6 @@ pref("toolkit.telemetry.firstShutdownPing.enabled", false);
pref("toolkit.telemetry.healthping.enabled", false);
pref("toolkit.telemetry.newProfilePing.enabled", false);
pref("toolkit.telemetry.eventping.enabled", false);
pref("toolkit.telemetry.ecosystemtelemetry.enabled", false);
pref("toolkit.telemetry.prioping.enabled", false);
pref("datareporting.policy.dataSubmissionEnabled", false);
pref("datareporting.healthreport.uploadEnabled", false);

View File

@ -47,9 +47,6 @@ XPCOMUtils.defineLazyGetter(this, "gStateFilePath", () => {
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
const SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID =
"deletion.request.ecosystem_client_id";
/**
* Checks if client ID has a valid format.
*
@ -78,16 +75,6 @@ var ClientID = Object.freeze({
return ClientIDImpl.getClientID();
},
/**
* Returns a promise resolving to the ecosystem client ID, used in ecosystem
* pings as a stable identifier for this profile.
*
* @return {Promise<string>} The ecosystem client ID.
*/
getEcosystemClientID() {
return ClientIDImpl.getEcosystemClientID();
},
/**
* This returns true if the client ID prior to the last client ID reset was a canary client ID.
* Android only. Always returns null on Desktop.
@ -111,62 +98,39 @@ var ClientID = Object.freeze({
return ClientIDImpl.getCachedClientID();
},
/**
* Gets the in-memory cached ecosystem client ID if it was already loaded;
* `null` otherwise.
*/
getCachedEcosystemClientID() {
return ClientIDImpl.getCachedEcosystemClientID();
},
async getClientIdHash() {
return ClientIDImpl.getClientIdHash();
},
/**
* Sets the main and ecosystem client IDs to the canary (known) client ID,
* writing them to disk and updating the cached versions.
* Sets the client ID to the canary (known) client ID,
* writing it to disk and updating the cached version.
*
* Use `removeClientIDs` followed by `get{Ecosystem}ClientID` to clear the
* existing IDs and generate new, random ones if required.
* Use `removeClientID` followed by `getClientID` to clear the
* existing ID and generate a new, random one if required.
*
* @return {Promise<void>}
*/
setCanaryClientIDs() {
return ClientIDImpl.setCanaryClientIDs();
setCanaryClientID() {
return ClientIDImpl.setCanaryClientID();
},
/**
* Sets the ecosystem client IDs to a new random value while leaving other IDs
* unchanged, writing the result to disk and updating the cached identifier.
* This can be used when a user signs out, to avoid linking telemetry between
* different accounts.
*
* Use `removeClientIDs` followed by `get{Ecosystem}ClientID` to reset *all* the
* identifiers rather than just the ecosystem client id.
*
* @return {Promise<void>} Resolves when the change has been saved to disk.
*/
resetEcosystemClientID() {
return ClientIDImpl.resetEcosystemClientID();
},
/**
* Clears the main and ecosystem client IDs asynchronously, removing them
* from disk. Use `getClientID()` and `getEcosystemClientID()` to generate
* fresh IDs after calling this method.
* Clears the client ID asynchronously, removing it
* from disk. Use `getClientID()` to generate
* a fresh ID after calling this method.
*
* Should only be used if a reset is explicitly requested by the user.
*
* @return {Promise<void>}
*/
removeClientIDs() {
return ClientIDImpl.removeClientIDs();
removeClientID() {
return ClientIDImpl.removeClientID();
},
/**
* Only used for testing. Invalidates the cached client IDs so that they're
* read again from file, but doesn't remove the existing IDs from disk.
* Only used for testing. Invalidates the cached client ID so that it is
* read again from file, but doesn't remove the existing ID from disk.
*/
_reset() {
return ClientIDImpl._reset();
@ -176,37 +140,34 @@ var ClientID = Object.freeze({
var ClientIDImpl = {
_clientID: null,
_clientIDHash: null,
_ecosystemClientID: null,
_loadClientIdsTask: null,
_saveClientIdsTask: null,
_removeClientIdsTask: null,
_loadClientIdTask: null,
_saveClientIdTask: null,
_removeClientIdTask: null,
_logger: null,
_wasCanary: null,
_loadClientIDs() {
if (this._loadClientIdsTask) {
return this._loadClientIdsTask;
_loadClientID() {
if (this._loadClientIdTask) {
return this._loadClientIdTask;
}
this._loadClientIdsTask = this._doLoadClientIDs();
let clear = () => (this._loadClientIdsTask = null);
this._loadClientIdsTask.then(clear, clear);
return this._loadClientIdsTask;
this._loadClientIdTask = this._doLoadClientID();
let clear = () => (this._loadClientIdTask = null);
this._loadClientIdTask.then(clear, clear);
return this._loadClientIdTask;
},
/**
* Load the client IDs (Telemetry Client ID and Ecosystem Client ID) from the
* DataReporting Service state file. If either ID is missing, we generate a
* new one.
* Load the client ID from the DataReporting Service state file. If it is
* missing, we generate a new one.
*/
async _doLoadClientIDs() {
this._log.trace(`_doLoadClientIDs`);
async _doLoadClientID() {
this._log.trace(`_doLoadClientID`);
// If there's a removal in progress, let's wait for it
await this._removeClientIdsTask;
await this._removeClientIdTask;
// Try to load the client id from the DRS state file.
let hasCurrentClientID = false;
let hasCurrentEcosystemClientID = false;
try {
let state = await CommonUtils.readJSON(gStateFilePath);
if (AppConstants.platform == "android" && state && "wasCanary" in state) {
@ -230,14 +191,10 @@ var ClientIDImpl = {
// This data collection's not that important.
}
hasCurrentClientID = this.updateClientID(state.clientID);
hasCurrentEcosystemClientID = this.updateEcosystemClientID(
state.ecosystemClientID
);
if (hasCurrentClientID && hasCurrentEcosystemClientID) {
this._log.trace(`_doLoadClientIDs: Client IDs loaded from state.`);
if (hasCurrentClientID) {
this._log.trace(`_doLoadClientID: Client IDs loaded from state.`);
return {
clientID: this._clientID,
ecosystemClientID: this._ecosystemClientID,
};
}
}
@ -256,27 +213,23 @@ var ClientIDImpl = {
}
}
// We're missing one or both IDs from the DRS state file and prefs.
// Generate new ones.
// We're missing the ID from the DRS state file and prefs.
// Generate a new one.
if (!hasCurrentClientID) {
Services.telemetry.scalarSet("telemetry.generated_new_client_id", true);
this.updateClientID(CommonUtils.generateUUID());
}
if (!hasCurrentEcosystemClientID) {
this.updateEcosystemClientID(CommonUtils.generateUUID());
}
this._saveClientIdsTask = this._saveClientIDs();
this._saveClientIdTask = this._saveClientID();
// Wait on persisting the id. Otherwise failure to save the ID would result in
// the client creating and subsequently sending multiple IDs to the server.
// This would appear as multiple clients submitting similar data, which would
// result in orphaning.
await this._saveClientIdsTask;
await this._saveClientIdTask;
this._log.trace("_doLoadClientIDs: New client IDs loaded and persisted.");
this._log.trace("_doLoadClientID: New client ID loaded and persisted.");
return {
clientID: this._clientID,
ecosystemClientID: this._ecosystemClientID,
};
},
@ -285,12 +238,11 @@ var ClientIDImpl = {
*
* @return {Promise} A promise resolved when the client ID is saved to disk.
*/
async _saveClientIDs() {
async _saveClientID() {
try {
this._log.trace(`_saveClientIDs`);
this._log.trace(`_saveClientID`);
let obj = {
clientID: this._clientID,
ecosystemClientID: this._ecosystemClientID,
};
// We detected a canary client ID when resetting, storing this as a flag
if (AppConstants.platform == "android" && this._wasCanary) {
@ -304,7 +256,7 @@ var ClientIDImpl = {
}
}
await CommonUtils.writeJSON(obj, gStateFilePath);
this._saveClientIdsTask = null;
this._saveClientIdTask = null;
} catch (ex) {
Services.telemetry.scalarAdd("telemetry.state_file_save_errors", 1);
throw ex;
@ -319,22 +271,13 @@ var ClientIDImpl = {
*/
async getClientID() {
if (!this._clientID) {
let { clientID } = await this._loadClientIDs();
let { clientID } = await this._loadClientID();
return clientID;
}
return Promise.resolve(this._clientID);
},
async getEcosystemClientID() {
if (!this._ecosystemClientID) {
let { ecosystemClientID } = await this._loadClientIDs();
return ecosystemClientID;
}
return Promise.resolve(this._ecosystemClientID);
},
/**
* This returns true if the client ID prior to the last client ID reset was a canary client ID.
* Android only. Always returns null on Desktop.
@ -387,10 +330,6 @@ var ClientIDImpl = {
return id;
},
getCachedEcosystemClientID() {
return this._ecosystemClientID;
},
async getClientIdHash() {
if (!this._clientIDHash) {
let byteArr = new TextEncoder().encode(await this.getClientID());
@ -402,70 +341,53 @@ var ClientIDImpl = {
},
/*
* Resets the provider. This is for testing only.
* Resets the module. This is for testing only.
*/
async _reset() {
await this._loadClientIdsTask;
await this._saveClientIdsTask;
await this._loadClientIdTask;
await this._saveClientIdTask;
this._clientID = null;
this._clientIDHash = null;
this._ecosystemClientID = null;
},
async setCanaryClientIDs() {
this._log.trace("setCanaryClientIDs");
async setCanaryClientID() {
this._log.trace("setCanaryClientID");
this.updateClientID(CANARY_CLIENT_ID);
this.updateEcosystemClientID(CANARY_CLIENT_ID);
this._saveClientIdsTask = this._saveClientIDs();
await this._saveClientIdsTask;
this._saveClientIdTask = this._saveClientID();
await this._saveClientIdTask;
return this._clientID;
},
async resetEcosystemClientID() {
this._log.trace("resetEcosystemClientID");
this.updateEcosystemClientID(CommonUtils.generateUUID());
this._saveClientIdsTask = this._saveClientIDs();
await this._saveClientIdsTask;
return this._ecosystemClientID;
},
async _doRemoveClientID() {
this._log.trace("_doRemoveClientID");
async _doRemoveClientIDs() {
this._log.trace("_doRemoveClientIDs");
// Reset the cached main and ecosystem client IDs.
// Reset the cached client ID.
this._clientID = null;
this._clientIDHash = null;
this._ecosystemClientID = null;
// Clear the client id from the preference cache.
Services.prefs.clearUserPref(PREF_CACHED_CLIENTID);
// Clear the old ecosystem client ID from the deletion request scalar store.
Services.telemetry.scalarSet(
SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID,
""
);
// If there is a save in progress, wait for it to complete.
await this._saveClientIdsTask;
await this._saveClientIdTask;
// Remove the client id from disk
// Remove the client-id-containing state file from disk
await IOUtils.remove(gStateFilePath);
},
async removeClientIDs() {
this._log.trace("removeClientIDs");
async removeClientID() {
this._log.trace("removeClientID");
Services.telemetry.scalarAdd("telemetry.removed_client_ids", 1);
let oldClientId = this._clientID;
// Wait for the removal.
// Asynchronous calls to getClientID will also be blocked on this.
this._removeClientIdsTask = this._doRemoveClientIDs();
let clear = () => (this._removeClientIdsTask = null);
this._removeClientIdsTask.then(clear, clear);
this._removeClientIdTask = this._doRemoveClientID();
let clear = () => (this._removeClientIdTask = null);
this._removeClientIdTask.then(clear, clear);
await this._removeClientIdsTask;
await this._removeClientIdTask;
// On Android we detect resets after a canary client ID.
if (AppConstants.platform == "android") {
@ -494,22 +416,6 @@ var ClientIDImpl = {
return true;
},
updateEcosystemClientID(id) {
if (!isValidClientID(id)) {
this._log.error(
"updateEcosystemClientID - invalid ecosystem client ID",
id
);
return false;
}
this._ecosystemClientID = id;
Services.telemetry.scalarSet(
SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID,
id
);
return true;
},
/**
* A helper for getting access to telemetry logger.
*/

View File

@ -78,7 +78,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
UpdatePing: "resource://gre/modules/UpdatePing.jsm",
TelemetryHealthPing: "resource://gre/modules/HealthPing.jsm",
TelemetryEventPing: "resource://gre/modules/EventPing.jsm",
EcosystemTelemetry: "resource://gre/modules/EcosystemTelemetry.jsm",
TelemetryPrioPing: "resource://gre/modules/PrioPing.jsm",
UninstallPing: "resource://gre/modules/UninstallPing.jsm",
OS: "resource://gre/modules/osfile.jsm",
@ -850,13 +849,13 @@ var Impl = {
this._log.trace(
"Upload enabled, but got canary client ID. Resetting."
);
await ClientID.removeClientIDs();
await ClientID.removeClientID();
this._clientID = await ClientID.getClientID();
} else if (!uploadEnabled && this._clientID != Utils.knownClientID) {
this._log.trace(
"Upload disabled, but got a valid client ID. Setting canary client ID."
);
await ClientID.setCanaryClientIDs();
await ClientID.setCanaryClientID();
this._clientID = await ClientID.getClientID();
}
@ -904,7 +903,6 @@ var Impl = {
}
TelemetryEventPing.startup();
EcosystemTelemetry.startup();
TelemetryPrioPing.startup();
if (uploadEnabled) {
@ -962,8 +960,6 @@ var Impl = {
this._shutdownStep = "Event" + now();
TelemetryEventPing.shutdown();
this._shutdownStep = "Ecosystem" + now();
EcosystemTelemetry.shutdown();
this._shutdownStep = "Prio" + now();
await TelemetryPrioPing.shutdown();
@ -1118,7 +1114,7 @@ var Impl = {
// Generate a new client ID and make sure this module uses the new version
let p = (async () => {
await ClientID.removeClientIDs();
await ClientID.removeClientID();
let id = await ClientID.getClientID();
this._clientID = id;
Telemetry.scalarSet("telemetry.data_upload_optin", true);
@ -1164,7 +1160,7 @@ var Impl = {
// 6. Set ClientID to a known value
let oldClientId = await ClientID.getClientID();
await ClientID.setCanaryClientIDs();
await ClientID.setCanaryClientID();
this._clientID = await ClientID.getClientID();
// 7. Send the deletion-request ping.

View File

@ -21,9 +21,6 @@ const { clearTimeout, setTimeout } = ChromeUtils.import(
"resource://gre/modules/Timer.jsm"
);
// Other pings
const { EcosystemTelemetry } = ChromeUtils.import(
"resource://gre/modules/EcosystemTelemetry.jsm"
);
const { TelemetryPrioPing } = ChromeUtils.import(
"resource://gre/modules/PrioPing.jsm"
);
@ -374,7 +371,6 @@ var TelemetryScheduler = {
this._log.trace("_schedulerTickLogic - Periodic ping due.");
this._lastPeriodicPingTime = now;
// Send other pings.
EcosystemTelemetry.periodicPing();
TelemetryPrioPing.periodicPing();
}

View File

@ -82,11 +82,6 @@ var TelemetryUtils = {
EventPingMinimumFrequency: "toolkit.telemetry.eventping.minimumFrequency",
EventPingMaximumFrequency: "toolkit.telemetry.eventping.maximumFrequency",
// Ecosystem Telemetry Preferences
EcosystemTelemetryEnabled: "toolkit.telemetry.ecosystemtelemetry.enabled",
EcosystemTelemetryAllowForNonProductionFxA:
"toolkit.telemetry.ecosystemtelemetry.allowForNonProductionFxA",
// Prio Ping Preferences
PrioPingEnabled: "toolkit.telemetry.prioping.enabled",
PrioPingDataLimit: "toolkit.telemetry.prioping.dataLimit",

View File

@ -148,20 +148,6 @@ Preferences
The maximum frequency at which an :doc:`../data/event-ping` will be sent.
Default is 10 (minutes).
``toolkit.telemetry.ecosystemtelemetry.enabled``
Whether :doc:`../data/ecosystem-telemetry` is enabled.
Default is false. Change requires restart.
``toolkit.telemetry.ecosystemtelemetry.allowForNonProductionFx``
Whether :doc:`../data/ecosystem-telemetry` will be submitted if Firefox is
configured to use non-production FxA servers. Non-production servers includes
servers run by Mozilla (eg, the "staging" or "dev" instances) and servers run
externally (eg, self-hosted users). The expectation is that this will
primarily be used for QA.
Default is false. Change requires restart.
``toolkit.telemetry.overrideUpdateChannel``
Override the ``channel`` value that is reported via Telemetry.

View File

@ -106,7 +106,6 @@ EXTRA_JS_MODULES += [
"app/TelemetryTimestamps.jsm",
"app/TelemetryUtils.jsm",
"pings/CoveragePing.jsm",
"pings/EcosystemTelemetry.jsm",
"pings/EventPing.jsm",
"pings/HealthPing.jsm",
"pings/ModulesPing.jsm",

View File

@ -1,406 +0,0 @@
/* 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/. */
/*
* This module sends the Telemetry Ecosystem pings periodically:
* https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/data/ecosystem-telemetry.html
*
* Note that ecosystem pings are only sent when the preference
* `toolkit.telemetry.ecosystemtelemetry.enabled` is set to `true` - eventually
* that will be the default, but you should check!
*
* Note also that these pings are currently only sent for users signed in to
* Firefox with a Firefox account.
*
* If you are using the non-production FxA stack, pings are not sent by default.
* To force them, you should set:
* - toolkit.telemetry.ecosystemtelemetry.allowForNonProductionFxA: true
*
* If you are trying to debug this, you might also find the following
* preferences useful:
* - toolkit.telemetry.log.level: "Trace"
* - toolkit.telemetry.log.dump: true
*/
"use strict";
var EXPORTED_SYMBOLS = ["EcosystemTelemetry"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
ONLOGIN_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
ONLOGOUT_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
ONVERIFIED_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
ON_PRELOGOUT_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
TelemetryController: "resource://gre/modules/TelemetryController.jsm",
TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm",
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
Log: "resource://gre/modules/Log.jsm",
Services: "resource://gre/modules/Services.jsm",
fxAccounts: "resource://gre/modules/FxAccounts.jsm",
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
ClientID: "resource://gre/modules/ClientID.jsm",
});
XPCOMUtils.defineLazyServiceGetters(this, {
Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
});
const LOGGER_NAME = "Toolkit.Telemetry";
const LOGGER_PREFIX = "EcosystemTelemetry::";
XPCOMUtils.defineLazyGetter(this, "log", () => {
return Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
});
var Policy = {
sendPing: (type, payload, options) =>
TelemetryController.submitExternalPing(type, payload, options),
monotonicNow: () => TelemetryUtils.monotonicNow(),
// Returns a promise that resolves with the Ecosystem anonymized id.
// Never rejects - will log an error and resolve with null on error.
async getEcosystemAnonId() {
try {
let userData = await fxAccounts.getSignedInUser();
if (!userData || !userData.verified) {
log.debug("No ecosystem anonymized ID - no user or unverified user");
return null;
}
return await fxAccounts.telemetry.ensureEcosystemAnonId();
} catch (ex) {
log.error("Failed to fetch the ecosystem anonymized ID", ex);
return null;
}
},
// Returns a promise that resolves with the current ecosystem client id.
getEcosystemClientId() {
return ClientID.getEcosystemClientID();
},
// Returns a promise that resolves when the ecosystem client id has been reset.
resetEcosystemClientId() {
return ClientID.resetEcosystemClientID();
},
};
var EcosystemTelemetry = {
Reason: Object.freeze({
PERIODIC: "periodic", // Send the ping in regular intervals
SHUTDOWN: "shutdown", // Send the ping on shutdown
LOGOUT: "logout", // Send after FxA logout
}),
PING_TYPE: "account-ecosystem",
METRICS_STORE: "account-ecosystem",
_lastSendTime: 0,
// Indicates that the Ecosystem ping is configured and ready to send pings.
_initialized: false,
// The promise returned by Policy.getEcosystemAnonId()
_promiseEcosystemAnonId: null,
// Sets up _promiseEcosystemAnonId in the hope that it will be resolved by the
// time we need it, and also already resolved when the user logs out.
prepareEcosystemAnonId() {
this._promiseEcosystemAnonId = Policy.getEcosystemAnonId();
},
enabled() {
// Never enabled when not Unified Telemetry (e.g. not enabled on Fennec)
// If not enabled, then it doesn't become enabled until the preferences
// are adjusted and the browser is restarted.
// Not enabled is different to "should I send pings?" - if enabled, then
// observers will still be setup so we are ready to transition from not
// sending pings into sending them.
if (
!Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false)
) {
return false;
}
if (
!Services.prefs.getBoolPref(
TelemetryUtils.Preferences.EcosystemTelemetryEnabled,
false
)
) {
return false;
}
if (
!FxAccounts.config.isProductionConfig() &&
!Services.prefs.getBoolPref(
TelemetryUtils.Preferences.EcosystemTelemetryAllowForNonProductionFxA,
false
)
) {
log.info("Ecosystem telemetry disabled due to FxA non-production user");
return false;
}
// We are enabled (although may or may not want to send pings.)
return true;
},
/**
* In what is an unfortunate level of coupling, FxA has hacks to call this
* function before it sends any account related notifications. This allows us
* to work correctly when logging out by ensuring we have the anonymized
* ecosystem ID by then (as *at* logout time it's too late)
*/
async prepareForFxANotification() {
// Telemetry might not have initialized yet, so make sure we have.
this.startup();
// We need to ensure the promise fetching the anon ecosystem id has
// resolved (but if we are pref'd off it will remain null.)
if (this._promiseEcosystemAnonId) {
await this._promiseEcosystemAnonId;
}
},
/**
* On startup, register all observers.
*/
startup() {
if (!this.enabled() || this._initialized) {
return;
}
log.trace("Starting up.");
// We "prime" the ecosystem id here - if it's not currently available, it
// will be done in the background, so should be ready by the time we
// actually need it.
this.prepareEcosystemAnonId();
this._addObservers();
this._initialized = true;
},
/**
* Shutdown this ping.
*
* This will send a final ping with the SHUTDOWN reason.
*/
shutdown() {
if (!this._initialized) {
return;
}
log.trace("Shutting down.");
this._submitPing(this.Reason.SHUTDOWN);
this._removeObservers();
this._initialized = false;
},
_addObservers() {
// FxA login, verification and logout.
Services.obs.addObserver(this, ONLOGIN_NOTIFICATION);
Services.obs.addObserver(this, ONVERIFIED_NOTIFICATION);
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION);
Services.obs.addObserver(this, ON_PRELOGOUT_NOTIFICATION);
},
_removeObservers() {
try {
// removeObserver may throw, which could interrupt shutdown.
Services.obs.removeObserver(this, ONLOGIN_NOTIFICATION);
Services.obs.removeObserver(this, ONVERIFIED_NOTIFICATION);
Services.obs.removeObserver(this, ONLOGOUT_NOTIFICATION);
Services.obs.removeObserver(this, ON_PRELOGOUT_NOTIFICATION);
} catch (ex) {}
},
observe(subject, topic, data) {
log.trace(`observe, topic: ${topic}`);
switch (topic) {
// This is a bit messy - an already verified user will get
// ONLOGIN_NOTIFICATION but *not* ONVERIFIED_NOTIFICATION. However, an
// unverified user can't do the ecosystem dance with the profile server.
// The only way to determine if the user is verified or not is via an
// async method, and this isn't async, so...
// Sadly, we just end up kicking off prepareEcosystemAnonId() twice in
// that scenario, which will typically be rare and is handled by FxA. Note
// also that we are just "priming" the ecosystem id here - if it's not
// currently available, it will be done in the background, so should be
// ready by the time we actually need it.
case ONLOGIN_NOTIFICATION:
case ONVERIFIED_NOTIFICATION:
// If we sent these pings for non-account users and this is a login
// notification, we'd want to submit now, so we have a fresh set of data
// for the user.
// But for now, all we need to do is start the promise to fetch the anon
// ID.
this.prepareEcosystemAnonId();
break;
case ONLOGOUT_NOTIFICATION:
// On logout we submit what we have, then switch to the "no anon id"
// state.
// Returns the promise for tests.
return this._submitPing(this.Reason.LOGOUT)
.then(async () => {
// Ensure _promiseEcosystemAnonId() is now going to resolve as null.
this.prepareEcosystemAnonId();
// Change the ecosystemClientId value on logout, so that if a different user signs in
// we cannot link the two anon_id values together via a shared client_id.
// (We are still confirming approval to perform such linking between accounts, and
// this code can be removed once confirmed).
await Policy.resetEcosystemClientId();
})
.catch(e => {
log.error("ONLOGOUT promise chain failed", e);
});
case ON_PRELOGOUT_NOTIFICATION:
// We don't need to do anything here - everything was done in startup.
// However, we keep this here so someone doesn't erroneously think the
// notification serves no purposes - it's the `observerPreloads` in
// FxAccounts that matters!
break;
}
return null;
},
// Called by TelemetryScheduler.jsm when periodic pings should be sent.
periodicPing() {
log.trace("periodic ping triggered");
return this._submitPing(this.Reason.PERIODIC);
},
/**
* Submit an ecosystem ping.
*
* It will not send a ping if Ecosystem Telemetry is disabled
* the module is not fully initialized or if the ping type is missing.
*
* It will automatically assemble the right payload and clear out Telemetry stores.
*
* @param {String} reason The reason we're sending the ping. One of TelemetryEcosystemPing.Reason.
*/
async _submitPing(reason) {
if (!this.enabled()) {
// It's possible we will end up here if FxA was using the production
// stack at startup but no longer is.
log.trace(`_submitPing was called, but ping is not enabled.`);
return;
}
if (!this._initialized) {
log.trace(`Not initialized when sending. Bug?`);
return;
}
log.trace(`_submitPing, reason: ${reason}`);
let now = Policy.monotonicNow();
// Duration in seconds
let duration = Math.round((now - this._lastSendTime) / 1000);
this._lastSendTime = now;
let payload = await this._payload(reason, duration);
if (!payload) {
// The reason for returning null will already have been logged.
return;
}
// Never include the client ID.
// We provide our own environment.
const options = {
addClientId: false,
addEnvironment: true,
overrideEnvironment: this._environment(),
usePingSender: reason === this.Reason.SHUTDOWN,
};
let id = await Policy.sendPing(this.PING_TYPE, payload, options);
log.info(`submitted ping ${id}`);
},
/**
* Assemble payload for a new ping
*
* @param {String} reason The reason we're sending the ping. One of TelemetryEcosystemPing.Reason.
* @param {Number} duration The duration since ping was last send in seconds.
*/
async _payload(reason, duration) {
let ecosystemAnonId = await this._promiseEcosystemAnonId;
if (!ecosystemAnonId) {
// This typically just means no user is logged in, so don't make too
// much noise.
log.info("Unable to determine the ecosystem anon id; skipping this ping");
return null;
}
let payload = {
reason,
ecosystemAnonId,
ecosystemClientId: await Policy.getEcosystemClientId(),
duration,
scalars: Telemetry.getSnapshotForScalars(
this.METRICS_STORE,
/* clear */ true,
/* filter test */ true
),
keyedScalars: Telemetry.getSnapshotForKeyedScalars(
this.METRICS_STORE,
/* clear */ true,
/* filter test */ true
),
histograms: Telemetry.getSnapshotForHistograms(
this.METRICS_STORE,
/* clear */ true,
/* filter test */ true
),
keyedHistograms: Telemetry.getSnapshotForKeyedHistograms(
this.METRICS_STORE,
/* clear */ true,
/* filter test */ true
),
};
return payload;
},
/**
* Get the minimal environment to include in the ping
*/
_environment() {
let currentEnv = TelemetryEnvironment.currentEnvironment;
let environment = {
settings: {
locale: currentEnv.settings.locale,
},
system: {
memoryMB: currentEnv.system.memoryMB,
os: {
name: currentEnv.system.os.name,
version: currentEnv.system.os.version,
locale: currentEnv.system.os.locale,
},
cpu: {
speedMHz: currentEnv.system.cpu.speedMHz,
},
},
profile: {}, // added conditionally
};
if (currentEnv.profile.creationDate) {
environment.profile.creationDate = currentEnv.profile.creationDate;
}
if (currentEnv.profile.firstUseDate) {
environment.profile.firstUseDate = currentEnv.profile.firstUseDate;
}
return environment;
},
testReset() {
this._initialized = false;
this._lastSendTime = 0;
this.startup();
},
};

View File

@ -527,13 +527,6 @@ if (runningInParent) {
// Speed up child process accumulations
Services.prefs.setIntPref(TelemetryUtils.Preferences.IPCBatchTimeout, 10);
// Make sure ecosystem telemetry is disabled, no matter which build
// Individual tests will enable it when appropriate
Services.prefs.setBoolPref(
TelemetryUtils.Preferences.EcosystemTelemetryEnabled,
false
);
// Non-unified Telemetry (e.g. Fennec on Android) needs the preference to be set
// in order to enable Telemetry.
if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false)) {

View File

@ -1,430 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
XPCOMUtils.defineLazyModuleGetters(this, {
ONLOGIN_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
ONLOGOUT_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
ONVERIFIED_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.js",
});
ChromeUtils.defineModuleGetter(
this,
"EcosystemTelemetry",
"resource://gre/modules/EcosystemTelemetry.jsm"
);
const TEST_PING_TYPE = "test-ping-type";
const RE_VALID_GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
function fakeIdleNotification(topic) {
let scheduler = ChromeUtils.import(
"resource://gre/modules/TelemetryScheduler.jsm",
null
);
return scheduler.TelemetryScheduler.observe(null, topic, null);
}
async function promiseNoPing() {
// We check there's not one of our pings pending by sending a test ping, then
// immediately fetching a pending ping and checking it's that test one.
TelemetryController.submitExternalPing(TEST_PING_TYPE, {}, {});
let ping = await PingServer.promiseNextPing();
Assert.equal(ping.type, TEST_PING_TYPE, "Should be a test ping.");
}
function checkPingStructure(ping, reason) {
Assert.equal(
ping.type,
EcosystemTelemetry.PING_TYPE,
"Should be an ecosystem ping."
);
Assert.ok(!("clientId" in ping), "Ping must not contain a client ID.");
Assert.ok("environment" in ping, "Ping must contain an environment.");
let environment = ping.environment;
// Check that the environment is indeed minimal
const ALLOWED_ENVIRONMENT_KEYS = ["settings", "system", "profile"];
Assert.deepEqual(
ALLOWED_ENVIRONMENT_KEYS,
Object.keys(environment),
"Environment should only contain a limited set of keys."
);
// Check that fields of the environment are indeed minimal
Assert.deepEqual(
["locale"],
Object.keys(environment.settings),
"Settings environment should only contain locale"
);
Assert.deepEqual(
["cpu", "memoryMB", "os"],
Object.keys(environment.system).sort(),
"System environment should contain a limited set of keys"
);
Assert.deepEqual(
["locale", "name", "version"],
Object.keys(environment.system.os).sort(),
"system.environment.os should contain a limited set of keys"
);
// Check the payload for required fields.
let payload = ping.payload;
Assert.equal(payload.reason, reason, "Ping reason must match.");
Assert.ok(
payload.duration >= 0,
"Payload must have a duration greater or equal to 0"
);
Assert.ok("ecosystemAnonId" in payload, "payload must have ecosystemAnonId");
Assert.ok(
RE_VALID_GUID.test(payload.ecosystemClientId),
"ecosystemClientId must be a valid GUID"
);
Assert.ok("scalars" in payload, "Payload must contain scalars");
Assert.ok("keyedScalars" in payload, "Payload must contain keyed scalars");
Assert.ok("histograms" in payload, "Payload must contain histograms");
Assert.ok(
"keyedHistograms" in payload,
"Payload must contain keyed histograms"
);
}
function fakeAnonId(fn) {
const m = ChromeUtils.import(
"resource://gre/modules/EcosystemTelemetry.jsm",
null
);
let oldFn = m.Policy.getEcosystemAnonId;
m.Policy.getEcosystemAnonId = fn;
return oldFn;
}
registerCleanupFunction(function() {
PingServer.stop();
});
add_task(async function setup() {
// Trigger a proper telemetry init.
do_get_profile(true);
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();
// Start the local ping server and setup Telemetry to use it during the tests.
PingServer.start();
Preferences.set(
TelemetryUtils.Preferences.Server,
"http://localhost:" + PingServer.port
);
TelemetrySend.setServer("http://localhost:" + PingServer.port);
await TelemetryController.testSetup();
});
// We make absolute sure the Ecosystem ping is never triggered on Fennec/Non-unified Telemetry
add_task(
{
skip_if: () => !gIsAndroid,
},
async function test_no_ecosystem_ping_on_fennec() {
// Force preference to true, we should have an additional check on Android/Unified Telemetry
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
// This is invoked in regular intervals by the timer.
// Would trigger ping sending.
EcosystemTelemetry.periodicPing();
await promiseNoPing();
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_disabled_non_fxa_production() {
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
Assert.ok(EcosystemTelemetry.enabled(), "enabled by default");
Preferences.set("identity.fxaccounts.autoconfig.uri", "http://");
Assert.ok(!EcosystemTelemetry.enabled(), "disabled if non-prod");
Preferences.set(
TelemetryUtils.Preferences.EcosystemTelemetryAllowForNonProductionFxA,
true
);
Assert.ok(
EcosystemTelemetry.enabled(),
"enabled for non-prod but preference override"
);
Preferences.reset("identity.fxaccounts.autoconfig.uri");
Preferences.reset(
TelemetryUtils.Preferences.EcosystemTelemetryAllowForNonProductionFxA
);
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_nosending_if_disabled() {
Preferences.set(
TelemetryUtils.Preferences.EcosystemTelemetryEnabled,
false
);
EcosystemTelemetry.testReset();
// This is invoked in regular intervals by the timer.
// Would trigger ping sending.
EcosystemTelemetry.periodicPing();
await promiseNoPing();
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_no_default_send() {
// No user's logged in, nothing is mocked, so nothing is sent.
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
// This is invoked in regular intervals by the timer.
EcosystemTelemetry.periodicPing();
await promiseNoPing();
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_login_workflow() {
// Fake the whole login/logout workflow by triggering the events directly.
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
let originalAnonId = fakeAnonId(() => null);
let ping;
// 1. No user, timer invoked
EcosystemTelemetry.periodicPing();
await promiseNoPing();
// 2. User logs in, but we fail to obtain a valid uid.
// No ping will be generated.
fakeAnonId(() => null);
EcosystemTelemetry.observe(null, ONLOGIN_NOTIFICATION, null);
EcosystemTelemetry.periodicPing();
await promiseNoPing();
// Once we've failed to get the ID, we don't try again until next startup
// or another login-related event - so...
// 3. uid becomes available after verification.
fakeAnonId(() => "test_login_workflow:my.anon.id");
EcosystemTelemetry.observe(null, ONVERIFIED_NOTIFICATION, null);
print("triggering ping now that we have an anon-id");
EcosystemTelemetry.periodicPing();
ping = await PingServer.promiseNextPing();
checkPingStructure(ping, "periodic");
Assert.equal(
ping.payload.ecosystemAnonId,
"test_login_workflow:my.anon.id"
);
const origClientId = ping.payload.ecosystemClientId;
// 4. User disconnects account, should get an immediate ping.
print("user disconnects");
// We need to arrange for the new empty anonid before the notification.
fakeAnonId(() => null);
await EcosystemTelemetry.observe(null, ONLOGOUT_NOTIFICATION, null);
ping = await PingServer.promiseNextPing();
checkPingStructure(ping, "logout");
Assert.equal(
ping.payload.ecosystemAnonId,
"test_login_workflow:my.anon.id",
"should have been submitted with the old anonid"
);
Assert.equal(
ping.payload.ecosystemClientId,
origClientId,
"should have been submitted with the old clientid"
);
Assert.equal(
await EcosystemTelemetry.promiseEcosystemAnonId,
null,
"should resolve to null immediately after logout"
);
// 5. No user, timer invoked
print("timer fires after disconnection");
EcosystemTelemetry.periodicPing();
await promiseNoPing();
// 6. Transition back to logged in, pings should again be sent.
fakeAnonId(() => "test_login_workflow:my.anon.id.2");
EcosystemTelemetry.observe(null, ONVERIFIED_NOTIFICATION, null);
print("triggering ping now the user has logged back in");
EcosystemTelemetry.periodicPing();
ping = await PingServer.promiseNextPing();
checkPingStructure(ping, "periodic");
Assert.equal(
ping.payload.ecosystemAnonId,
"test_login_workflow:my.anon.id.2"
);
Assert.notEqual(
ping.payload.ecosystemClientId,
origClientId,
"should have a different clientid after signing out then back in"
);
// Reset policy.
fakeAnonId(originalAnonId);
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_shutdown_logged_in() {
// Check shutdown when a user's logged in does the right thing.
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
let originalAnonId = fakeAnonId(() =>
Promise.resolve("test_shutdown_logged_in:my.anon.id")
);
EcosystemTelemetry.observe(null, ONLOGIN_NOTIFICATION, null);
// No ping expected yet.
await promiseNoPing();
// Shutdown
EcosystemTelemetry.shutdown();
let ping = await PingServer.promiseNextPing();
checkPingStructure(ping, "shutdown");
Assert.equal(
ping.payload.ecosystemAnonId,
"test_shutdown_logged_in:my.anon.id",
"our anon ID is in the ping"
);
fakeAnonId(originalAnonId);
}
);
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_shutdown_not_logged_in() {
// Check shutdown when no user is logged in does the right thing.
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
let originalAnonId = fakeAnonId(() => Promise.resolve(null));
// No ping expected yet.
await promiseNoPing();
// Shutdown
EcosystemTelemetry.shutdown();
// Still no ping.
await promiseNoPing();
fakeAnonId(originalAnonId);
}
);
// Test that a periodic ping is triggered by the scheduler at midnight
//
// Based on `test_TelemetrySession#test_DailyDueAndIdle`.
add_task(
{
skip_if: () => gIsAndroid,
},
async function test_periodic_ping() {
await TelemetryStorage.testClearPendingPings();
PingServer.clearRequests();
let receivedPing = null;
// Register a ping handler that will assert when receiving multiple ecosystem pings.
// We can ignore other pings, such as the periodic ping.
PingServer.registerPingHandler(req => {
const ping = decodeRequestPayload(req);
if (ping.type == EcosystemTelemetry.PING_TYPE) {
Assert.ok(
!receivedPing,
"Telemetry must only send one periodic ecosystem ping."
);
receivedPing = ping;
}
});
// Faking scheduler timer has to happen before resetting TelemetryController
// to be effective.
let schedulerTickCallback = null;
let now = new Date(2040, 1, 1, 0, 0, 0);
fakeNow(now);
// Fake scheduler functions to control periodic collection flow in tests.
fakeSchedulerTimer(
callback => (schedulerTickCallback = callback),
() => {}
);
await TelemetryController.testReset();
Preferences.set(TelemetryUtils.Preferences.EcosystemTelemetryEnabled, true);
EcosystemTelemetry.testReset();
// Have to arrange for an anon-id to be configured.
let originalAnonId = fakeAnonId(() => "test_periodic_ping:my.anon.id");
EcosystemTelemetry.observe(null, ONLOGIN_NOTIFICATION, null);
// As a sanity check we trigger a keyedHistogram and scalar declared as
// being in our ping, just to help ensure that the payload was assembled
// in the correct shape.
let h = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
h.add("test-key");
Telemetry.scalarSet("browser.engagement.total_uri_count", 2);
// Trigger the periodic ecosystem ping.
let firstPeriodicDue = new Date(2040, 1, 2, 0, 0, 0);
fakeNow(firstPeriodicDue);
// Run a scheduler tick: it should trigger the periodic ping.
Assert.ok(!!schedulerTickCallback);
let tickPromise = schedulerTickCallback();
// Send an idle and then an active user notification.
fakeIdleNotification("idle");
fakeIdleNotification("active");
// Wait on the tick promise.
await tickPromise;
await TelemetrySend.testWaitOnOutgoingPings();
// Decode the ping contained in the request and check that's a periodic ping.
Assert.ok(receivedPing, "Telemetry must send one ecosystem periodic ping.");
checkPingStructure(receivedPing, "periodic");
// And check the content we expect is there.
Assert.ok(receivedPing.payload.keyedHistograms.parent.SEARCH_COUNTS);
Assert.equal(
receivedPing.payload.scalars.parent["browser.engagement.total_uri_count"],
2
);
fakeAnonId(originalAnonId);
}
);

View File

@ -265,8 +265,6 @@ add_task(async function test_disableDataUpload() {
secondClientId,
"The client id must have changed"
);
let secondEcosystemClientId = await ClientID.getEcosystemClientID();
// Simulate a failure in sending the deletion-request ping by disabling the HTTP server.
await PingServer.stop();
@ -338,15 +336,6 @@ add_task(async function test_disableDataUpload() {
ping.clientId,
"Deletion must be requested for correct client id"
);
if (AppConstants.MOZ_APP_NAME != "thunderbird") {
// We don't record the old ecosystem client ID on Thunderbird,
// since the FxA and telemetry infrastructure is different there.
Assert.equal(
secondEcosystemClientId,
ping.payload.scalars.parent["deletion.request.ecosystem_client_id"],
"Deletion must be requested for correct ecosystem client ID"
);
}
// Wait on ping activity to settle before moving on to the next test. If we were
// to shut down telemetry, even though the PingServer caught the expected pings,

View File

@ -11,9 +11,6 @@ const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
const SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID =
"deletion.request.ecosystem_client_id";
var drsPath;
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@ -37,33 +34,6 @@ function run_test() {
run_next_test();
}
add_task(async function test_ecosystemClientID() {
await ClientID._reset();
Assert.ok(!ClientID.getCachedEcosystemClientID());
let ecosystemClientID = await ClientID.getEcosystemClientID();
Assert.equal(typeof ecosystemClientID, "string");
Assert.equal(ClientID.getCachedEcosystemClientID(), ecosystemClientID);
let clientID = await ClientID.getClientID();
await ClientID._reset();
await OS.File.writeAtomic(
drsPath,
JSON.stringify({
clientID,
}),
{
encoding: "utf-8",
tmpPath: drsPath + ".tmp",
}
);
let newClientID = await ClientID.getClientID();
Assert.equal(newClientID, clientID);
let newEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.notEqual(newEcosystemClientID, ecosystemClientID);
});
add_task(async function test_client_id() {
const invalidIDs = [
[-1, "setIntPref"],
@ -169,121 +139,27 @@ add_task(async function test_client_id() {
}
});
add_task(async function test_setCanaryClientIDs() {
add_task(async function test_setCanaryClientID() {
const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
await ClientID._reset();
// We should be able to set a valid UUID
await ClientID.setCanaryClientIDs();
await ClientID.setCanaryClientID();
let clientID = await ClientID.getClientID();
Assert.equal(KNOWN_UUID, clientID);
});
add_task(async function test_resetEcosystemClientID() {
await ClientID._reset();
let firstClientID = await ClientID.getClientID();
let firstEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.ok(firstClientID);
Assert.ok(firstEcosystemClientID);
// We should reset the ecosystem client id, but not the main client id.
await ClientID.resetEcosystemClientID();
let secondClientID = await ClientID.getClientID();
let secondEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.equal(firstClientID, secondClientID);
Assert.notEqual(firstEcosystemClientID, secondEcosystemClientID);
// The new id should have been persisted to disk.
await ClientID._reset();
let thirdClientID = await ClientID.getClientID();
let thirdEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.equal(thirdClientID, secondClientID);
Assert.equal(thirdEcosystemClientID, secondEcosystemClientID);
});
add_task(async function test_removeClientIDs() {
// We should get a valid UUID after reset
await ClientID._reset();
let firstClientID = await ClientID.getClientID();
let firstEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.equal(typeof firstClientID, "string");
Assert.equal(typeof firstEcosystemClientID, "string");
Assert.ok(uuidRegex.test(firstClientID));
Assert.ok(uuidRegex.test(firstEcosystemClientID));
await ClientID.removeClientIDs();
if (
AppConstants.platform != "android" &&
AppConstants.MOZ_APP_NAME != "thunderbird"
) {
// We don't record the old ecosystem client ID on Android or Thunderbird,
// since the FxA and telemetry infrastructure is different there.
let prefClientID = Services.prefs.getStringPref(PREF_CACHED_CLIENTID, null);
let scalarsDeletionRequest = Services.telemetry.getSnapshotForScalars(
"deletion-request"
);
Assert.ok(!prefClientID);
Assert.ok(
!scalarsDeletionRequest.parent?.[
SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID
]
);
}
// When resetting again we should get a new ID
let nextClientID = await ClientID.getClientID();
let nextEcosystemClientID = await ClientID.getEcosystemClientID();
Assert.equal(typeof nextClientID, "string");
Assert.equal(typeof nextEcosystemClientID, "string");
Assert.ok(uuidRegex.test(nextClientID));
Assert.ok(uuidRegex.test(nextEcosystemClientID));
Assert.notEqual(
firstClientID,
nextClientID,
"After reset client ID should be different."
);
Assert.notEqual(
firstEcosystemClientID,
nextEcosystemClientID,
"After reset ecosystem client ID should be different."
);
let cachedID = ClientID.getCachedClientID();
Assert.equal(nextClientID, cachedID);
let cachedEcosystemID = ClientID.getCachedEcosystemClientID();
Assert.equal(nextEcosystemClientID, cachedEcosystemID);
let prefClientID = Services.prefs.getStringPref(PREF_CACHED_CLIENTID, null);
Assert.equal(nextClientID, prefClientID);
if (
AppConstants.platform != "android" &&
AppConstants.MOZ_APP_NAME != "thunderbird"
) {
let scalarsDeletionRequest = Services.telemetry.getSnapshotForScalars(
"deletion-request"
);
Assert.equal(
nextEcosystemClientID,
scalarsDeletionRequest.parent[SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID]
);
}
});
add_task(async function test_removeParallelGet() {
// We should get a valid UUID after reset
await ClientID.removeClientIDs();
await ClientID.removeClientID();
let firstClientID = await ClientID.getClientID();
// We should get the same ID twice when requesting it in parallel to a reset.
let promiseRemoveClientIDs = ClientID.removeClientIDs();
let promiseRemoveClientID = ClientID.removeClientID();
let p = ClientID.getClientID();
let newClientID = await ClientID.getClientID();
await promiseRemoveClientIDs;
await promiseRemoveClientID;
let otherClientID = await p;
Assert.notEqual(
@ -306,19 +182,19 @@ add_task(
const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
// We should get a valid UUID after reset
await ClientID.removeClientIDs();
await ClientID.removeClientID();
let firstClientID = await ClientID.getClientID();
Assert.notEqual(KNOWN_UUID, firstClientID, "Client ID should be random.");
// Set the canary client ID.
await ClientID.setCanaryClientIDs();
await ClientID.setCanaryClientID();
Assert.equal(
KNOWN_UUID,
await ClientID.getClientID(),
"Client ID should be known canary."
);
await ClientID.removeClientIDs();
await ClientID.removeClientID();
let newClientID = await ClientID.getClientID();
Assert.notEqual(
KNOWN_UUID,
@ -335,7 +211,7 @@ add_task(
"After reset we should have detected a canary client ID"
);
await ClientID.removeClientIDs();
await ClientID.removeClientID();
let clientID = await ClientID.getClientID();
Assert.notEqual(
KNOWN_UUID,

View File

@ -91,8 +91,6 @@ skip-if = (os == "android") || (os == "linux" && bits == 32)
[test_TelemetryUtils.js]
[test_ThirdPartyModulesPing.js]
run-if = nightly_build && (os == 'win')
[test_EcosystemTelemetry.js]
skip-if = appname == "thunderbird"
[test_EventPing.js]
tags = coverage
[test_CoveragePing.js]