mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1697555 - Remove Telemetry portions of Accounts Ecosystem Telemetry r=Dexter
Differential Revision: https://phabricator.services.mozilla.com/D110091
This commit is contained in:
parent
2aac69d13c
commit
ea2a18d757
@ -612,7 +612,6 @@ module.exports = {
|
|||||||
"toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js",
|
"toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js",
|
||||||
"toolkit/components/processsingleton/MainProcessSingleton.jsm",
|
"toolkit/components/processsingleton/MainProcessSingleton.jsm",
|
||||||
"toolkit/components/telemetry/tests/unit/head.js",
|
"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_EventPing.js",
|
||||||
"toolkit/components/telemetry/tests/unit/test_HealthPing.js",
|
"toolkit/components/telemetry/tests/unit/test_HealthPing.js",
|
||||||
"toolkit/components/telemetry/tests/unit/test_PingAPI.js",
|
"toolkit/components/telemetry/tests/unit/test_PingAPI.js",
|
||||||
|
@ -1681,8 +1681,6 @@ pref("toolkit.telemetry.newProfilePing.enabled", true);
|
|||||||
pref("toolkit.telemetry.updatePing.enabled", true);
|
pref("toolkit.telemetry.updatePing.enabled", true);
|
||||||
// Enables sending 'bhr' pings when the browser hangs.
|
// Enables sending 'bhr' pings when the browser hangs.
|
||||||
pref("toolkit.telemetry.bhrPing.enabled", true);
|
pref("toolkit.telemetry.bhrPing.enabled", true);
|
||||||
// Whether to enable Ecosystem Telemetry, requires a restart.
|
|
||||||
pref("toolkit.telemetry.ecosystemtelemetry.enabled", false);
|
|
||||||
|
|
||||||
// Ping Centre Telemetry settings.
|
// Ping Centre Telemetry settings.
|
||||||
pref("browser.ping-centre.telemetry", true);
|
pref("browser.ping-centre.telemetry", true);
|
||||||
|
@ -126,7 +126,7 @@ add_task(async function test_discovery() {
|
|||||||
equal(cookie.host, uri.host, "cookie exists for host");
|
equal(cookie.host, uri.host, "cookie exists for host");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
await ClientID.getClientID();
|
await ClientID.getClientID();
|
||||||
await changed;
|
await changed;
|
||||||
|
|
||||||
|
@ -68,7 +68,6 @@ class PingServer(Layer):
|
|||||||
"toolkit.telemetry.enabled": True,
|
"toolkit.telemetry.enabled": True,
|
||||||
"toolkit.telemetry.unified": True,
|
"toolkit.telemetry.unified": True,
|
||||||
"toolkit.telemetry.shutdownPingSender.enabled": True,
|
"toolkit.telemetry.shutdownPingSender.enabled": True,
|
||||||
"toolkit.telemetry.ecosystemtelemetry.enabled": True,
|
|
||||||
"datareporting.policy.dataSubmissionPolicyBypassNotification": True,
|
"datareporting.policy.dataSubmissionPolicyBypassNotification": True,
|
||||||
"toolkit.telemetry.send.overrideOfficialCheck": True,
|
"toolkit.telemetry.send.overrideOfficialCheck": True,
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ pref("toolkit.telemetry.firstShutdownPing.enabled", false);
|
|||||||
pref("toolkit.telemetry.healthping.enabled", false);
|
pref("toolkit.telemetry.healthping.enabled", false);
|
||||||
pref("toolkit.telemetry.newProfilePing.enabled", false);
|
pref("toolkit.telemetry.newProfilePing.enabled", false);
|
||||||
pref("toolkit.telemetry.eventping.enabled", false);
|
pref("toolkit.telemetry.eventping.enabled", false);
|
||||||
pref("toolkit.telemetry.ecosystemtelemetry.enabled", false);
|
|
||||||
pref("toolkit.telemetry.prioping.enabled", false);
|
pref("toolkit.telemetry.prioping.enabled", false);
|
||||||
pref("datareporting.policy.dataSubmissionEnabled", false);
|
pref("datareporting.policy.dataSubmissionEnabled", false);
|
||||||
pref("datareporting.healthreport.uploadEnabled", false);
|
pref("datareporting.healthreport.uploadEnabled", false);
|
||||||
|
@ -47,9 +47,6 @@ XPCOMUtils.defineLazyGetter(this, "gStateFilePath", () => {
|
|||||||
|
|
||||||
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
|
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.
|
* Checks if client ID has a valid format.
|
||||||
*
|
*
|
||||||
@ -78,16 +75,6 @@ var ClientID = Object.freeze({
|
|||||||
return ClientIDImpl.getClientID();
|
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.
|
* 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.
|
* Android only. Always returns null on Desktop.
|
||||||
@ -111,62 +98,39 @@ var ClientID = Object.freeze({
|
|||||||
return ClientIDImpl.getCachedClientID();
|
return ClientIDImpl.getCachedClientID();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the in-memory cached ecosystem client ID if it was already loaded;
|
|
||||||
* `null` otherwise.
|
|
||||||
*/
|
|
||||||
getCachedEcosystemClientID() {
|
|
||||||
return ClientIDImpl.getCachedEcosystemClientID();
|
|
||||||
},
|
|
||||||
|
|
||||||
async getClientIdHash() {
|
async getClientIdHash() {
|
||||||
return ClientIDImpl.getClientIdHash();
|
return ClientIDImpl.getClientIdHash();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the main and ecosystem client IDs to the canary (known) client ID,
|
* Sets the client ID to the canary (known) client ID,
|
||||||
* writing them to disk and updating the cached versions.
|
* writing it to disk and updating the cached version.
|
||||||
*
|
*
|
||||||
* Use `removeClientIDs` followed by `get{Ecosystem}ClientID` to clear the
|
* Use `removeClientID` followed by `getClientID` to clear the
|
||||||
* existing IDs and generate new, random ones if required.
|
* existing ID and generate a new, random one if required.
|
||||||
*
|
*
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
setCanaryClientIDs() {
|
setCanaryClientID() {
|
||||||
return ClientIDImpl.setCanaryClientIDs();
|
return ClientIDImpl.setCanaryClientID();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the ecosystem client IDs to a new random value while leaving other IDs
|
* Clears the client ID asynchronously, removing it
|
||||||
* unchanged, writing the result to disk and updating the cached identifier.
|
* from disk. Use `getClientID()` to generate
|
||||||
* This can be used when a user signs out, to avoid linking telemetry between
|
* a fresh ID after calling this method.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Should only be used if a reset is explicitly requested by the user.
|
* Should only be used if a reset is explicitly requested by the user.
|
||||||
*
|
*
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
removeClientIDs() {
|
removeClientID() {
|
||||||
return ClientIDImpl.removeClientIDs();
|
return ClientIDImpl.removeClientID();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only used for testing. Invalidates the cached client IDs so that they're
|
* Only used for testing. Invalidates the cached client ID so that it is
|
||||||
* read again from file, but doesn't remove the existing IDs from disk.
|
* read again from file, but doesn't remove the existing ID from disk.
|
||||||
*/
|
*/
|
||||||
_reset() {
|
_reset() {
|
||||||
return ClientIDImpl._reset();
|
return ClientIDImpl._reset();
|
||||||
@ -176,37 +140,34 @@ var ClientID = Object.freeze({
|
|||||||
var ClientIDImpl = {
|
var ClientIDImpl = {
|
||||||
_clientID: null,
|
_clientID: null,
|
||||||
_clientIDHash: null,
|
_clientIDHash: null,
|
||||||
_ecosystemClientID: null,
|
_loadClientIdTask: null,
|
||||||
_loadClientIdsTask: null,
|
_saveClientIdTask: null,
|
||||||
_saveClientIdsTask: null,
|
_removeClientIdTask: null,
|
||||||
_removeClientIdsTask: null,
|
|
||||||
_logger: null,
|
_logger: null,
|
||||||
_wasCanary: null,
|
_wasCanary: null,
|
||||||
|
|
||||||
_loadClientIDs() {
|
_loadClientID() {
|
||||||
if (this._loadClientIdsTask) {
|
if (this._loadClientIdTask) {
|
||||||
return this._loadClientIdsTask;
|
return this._loadClientIdTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadClientIdsTask = this._doLoadClientIDs();
|
this._loadClientIdTask = this._doLoadClientID();
|
||||||
let clear = () => (this._loadClientIdsTask = null);
|
let clear = () => (this._loadClientIdTask = null);
|
||||||
this._loadClientIdsTask.then(clear, clear);
|
this._loadClientIdTask.then(clear, clear);
|
||||||
return this._loadClientIdsTask;
|
return this._loadClientIdTask;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the client IDs (Telemetry Client ID and Ecosystem Client ID) from the
|
* Load the client ID from the DataReporting Service state file. If it is
|
||||||
* DataReporting Service state file. If either ID is missing, we generate a
|
* missing, we generate a new one.
|
||||||
* new one.
|
|
||||||
*/
|
*/
|
||||||
async _doLoadClientIDs() {
|
async _doLoadClientID() {
|
||||||
this._log.trace(`_doLoadClientIDs`);
|
this._log.trace(`_doLoadClientID`);
|
||||||
// If there's a removal in progress, let's wait for it
|
// 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.
|
// Try to load the client id from the DRS state file.
|
||||||
let hasCurrentClientID = false;
|
let hasCurrentClientID = false;
|
||||||
let hasCurrentEcosystemClientID = false;
|
|
||||||
try {
|
try {
|
||||||
let state = await CommonUtils.readJSON(gStateFilePath);
|
let state = await CommonUtils.readJSON(gStateFilePath);
|
||||||
if (AppConstants.platform == "android" && state && "wasCanary" in state) {
|
if (AppConstants.platform == "android" && state && "wasCanary" in state) {
|
||||||
@ -230,14 +191,10 @@ var ClientIDImpl = {
|
|||||||
// This data collection's not that important.
|
// This data collection's not that important.
|
||||||
}
|
}
|
||||||
hasCurrentClientID = this.updateClientID(state.clientID);
|
hasCurrentClientID = this.updateClientID(state.clientID);
|
||||||
hasCurrentEcosystemClientID = this.updateEcosystemClientID(
|
if (hasCurrentClientID) {
|
||||||
state.ecosystemClientID
|
this._log.trace(`_doLoadClientID: Client IDs loaded from state.`);
|
||||||
);
|
|
||||||
if (hasCurrentClientID && hasCurrentEcosystemClientID) {
|
|
||||||
this._log.trace(`_doLoadClientIDs: Client IDs loaded from state.`);
|
|
||||||
return {
|
return {
|
||||||
clientID: this._clientID,
|
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.
|
// We're missing the ID from the DRS state file and prefs.
|
||||||
// Generate new ones.
|
// Generate a new one.
|
||||||
if (!hasCurrentClientID) {
|
if (!hasCurrentClientID) {
|
||||||
Services.telemetry.scalarSet("telemetry.generated_new_client_id", true);
|
Services.telemetry.scalarSet("telemetry.generated_new_client_id", true);
|
||||||
this.updateClientID(CommonUtils.generateUUID());
|
this.updateClientID(CommonUtils.generateUUID());
|
||||||
}
|
}
|
||||||
if (!hasCurrentEcosystemClientID) {
|
this._saveClientIdTask = this._saveClientID();
|
||||||
this.updateEcosystemClientID(CommonUtils.generateUUID());
|
|
||||||
}
|
|
||||||
this._saveClientIdsTask = this._saveClientIDs();
|
|
||||||
|
|
||||||
// Wait on persisting the id. Otherwise failure to save the ID would result in
|
// 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.
|
// the client creating and subsequently sending multiple IDs to the server.
|
||||||
// This would appear as multiple clients submitting similar data, which would
|
// This would appear as multiple clients submitting similar data, which would
|
||||||
// result in orphaning.
|
// 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 {
|
return {
|
||||||
clientID: this._clientID,
|
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.
|
* @return {Promise} A promise resolved when the client ID is saved to disk.
|
||||||
*/
|
*/
|
||||||
async _saveClientIDs() {
|
async _saveClientID() {
|
||||||
try {
|
try {
|
||||||
this._log.trace(`_saveClientIDs`);
|
this._log.trace(`_saveClientID`);
|
||||||
let obj = {
|
let obj = {
|
||||||
clientID: this._clientID,
|
clientID: this._clientID,
|
||||||
ecosystemClientID: this._ecosystemClientID,
|
|
||||||
};
|
};
|
||||||
// We detected a canary client ID when resetting, storing this as a flag
|
// We detected a canary client ID when resetting, storing this as a flag
|
||||||
if (AppConstants.platform == "android" && this._wasCanary) {
|
if (AppConstants.platform == "android" && this._wasCanary) {
|
||||||
@ -304,7 +256,7 @@ var ClientIDImpl = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await CommonUtils.writeJSON(obj, gStateFilePath);
|
await CommonUtils.writeJSON(obj, gStateFilePath);
|
||||||
this._saveClientIdsTask = null;
|
this._saveClientIdTask = null;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Services.telemetry.scalarAdd("telemetry.state_file_save_errors", 1);
|
Services.telemetry.scalarAdd("telemetry.state_file_save_errors", 1);
|
||||||
throw ex;
|
throw ex;
|
||||||
@ -319,22 +271,13 @@ var ClientIDImpl = {
|
|||||||
*/
|
*/
|
||||||
async getClientID() {
|
async getClientID() {
|
||||||
if (!this._clientID) {
|
if (!this._clientID) {
|
||||||
let { clientID } = await this._loadClientIDs();
|
let { clientID } = await this._loadClientID();
|
||||||
return clientID;
|
return clientID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(this._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.
|
* 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.
|
* Android only. Always returns null on Desktop.
|
||||||
@ -387,10 +330,6 @@ var ClientIDImpl = {
|
|||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
|
|
||||||
getCachedEcosystemClientID() {
|
|
||||||
return this._ecosystemClientID;
|
|
||||||
},
|
|
||||||
|
|
||||||
async getClientIdHash() {
|
async getClientIdHash() {
|
||||||
if (!this._clientIDHash) {
|
if (!this._clientIDHash) {
|
||||||
let byteArr = new TextEncoder().encode(await this.getClientID());
|
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() {
|
async _reset() {
|
||||||
await this._loadClientIdsTask;
|
await this._loadClientIdTask;
|
||||||
await this._saveClientIdsTask;
|
await this._saveClientIdTask;
|
||||||
this._clientID = null;
|
this._clientID = null;
|
||||||
this._clientIDHash = null;
|
this._clientIDHash = null;
|
||||||
this._ecosystemClientID = null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async setCanaryClientIDs() {
|
async setCanaryClientID() {
|
||||||
this._log.trace("setCanaryClientIDs");
|
this._log.trace("setCanaryClientID");
|
||||||
this.updateClientID(CANARY_CLIENT_ID);
|
this.updateClientID(CANARY_CLIENT_ID);
|
||||||
this.updateEcosystemClientID(CANARY_CLIENT_ID);
|
|
||||||
|
|
||||||
this._saveClientIdsTask = this._saveClientIDs();
|
this._saveClientIdTask = this._saveClientID();
|
||||||
await this._saveClientIdsTask;
|
await this._saveClientIdTask;
|
||||||
return this._clientID;
|
return this._clientID;
|
||||||
},
|
},
|
||||||
|
|
||||||
async resetEcosystemClientID() {
|
async _doRemoveClientID() {
|
||||||
this._log.trace("resetEcosystemClientID");
|
this._log.trace("_doRemoveClientID");
|
||||||
this.updateEcosystemClientID(CommonUtils.generateUUID());
|
|
||||||
this._saveClientIdsTask = this._saveClientIDs();
|
|
||||||
await this._saveClientIdsTask;
|
|
||||||
return this._ecosystemClientID;
|
|
||||||
},
|
|
||||||
|
|
||||||
async _doRemoveClientIDs() {
|
// Reset the cached client ID.
|
||||||
this._log.trace("_doRemoveClientIDs");
|
|
||||||
|
|
||||||
// Reset the cached main and ecosystem client IDs.
|
|
||||||
this._clientID = null;
|
this._clientID = null;
|
||||||
this._clientIDHash = null;
|
this._clientIDHash = null;
|
||||||
this._ecosystemClientID = null;
|
|
||||||
|
|
||||||
// Clear the client id from the preference cache.
|
// Clear the client id from the preference cache.
|
||||||
Services.prefs.clearUserPref(PREF_CACHED_CLIENTID);
|
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.
|
// 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);
|
await IOUtils.remove(gStateFilePath);
|
||||||
},
|
},
|
||||||
|
|
||||||
async removeClientIDs() {
|
async removeClientID() {
|
||||||
this._log.trace("removeClientIDs");
|
this._log.trace("removeClientID");
|
||||||
Services.telemetry.scalarAdd("telemetry.removed_client_ids", 1);
|
Services.telemetry.scalarAdd("telemetry.removed_client_ids", 1);
|
||||||
let oldClientId = this._clientID;
|
let oldClientId = this._clientID;
|
||||||
|
|
||||||
// Wait for the removal.
|
// Wait for the removal.
|
||||||
// Asynchronous calls to getClientID will also be blocked on this.
|
// Asynchronous calls to getClientID will also be blocked on this.
|
||||||
this._removeClientIdsTask = this._doRemoveClientIDs();
|
this._removeClientIdTask = this._doRemoveClientID();
|
||||||
let clear = () => (this._removeClientIdsTask = null);
|
let clear = () => (this._removeClientIdTask = null);
|
||||||
this._removeClientIdsTask.then(clear, clear);
|
this._removeClientIdTask.then(clear, clear);
|
||||||
|
|
||||||
await this._removeClientIdsTask;
|
await this._removeClientIdTask;
|
||||||
|
|
||||||
// On Android we detect resets after a canary client ID.
|
// On Android we detect resets after a canary client ID.
|
||||||
if (AppConstants.platform == "android") {
|
if (AppConstants.platform == "android") {
|
||||||
@ -494,22 +416,6 @@ var ClientIDImpl = {
|
|||||||
return true;
|
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.
|
* A helper for getting access to telemetry logger.
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +78,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
UpdatePing: "resource://gre/modules/UpdatePing.jsm",
|
UpdatePing: "resource://gre/modules/UpdatePing.jsm",
|
||||||
TelemetryHealthPing: "resource://gre/modules/HealthPing.jsm",
|
TelemetryHealthPing: "resource://gre/modules/HealthPing.jsm",
|
||||||
TelemetryEventPing: "resource://gre/modules/EventPing.jsm",
|
TelemetryEventPing: "resource://gre/modules/EventPing.jsm",
|
||||||
EcosystemTelemetry: "resource://gre/modules/EcosystemTelemetry.jsm",
|
|
||||||
TelemetryPrioPing: "resource://gre/modules/PrioPing.jsm",
|
TelemetryPrioPing: "resource://gre/modules/PrioPing.jsm",
|
||||||
UninstallPing: "resource://gre/modules/UninstallPing.jsm",
|
UninstallPing: "resource://gre/modules/UninstallPing.jsm",
|
||||||
OS: "resource://gre/modules/osfile.jsm",
|
OS: "resource://gre/modules/osfile.jsm",
|
||||||
@ -850,13 +849,13 @@ var Impl = {
|
|||||||
this._log.trace(
|
this._log.trace(
|
||||||
"Upload enabled, but got canary client ID. Resetting."
|
"Upload enabled, but got canary client ID. Resetting."
|
||||||
);
|
);
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
this._clientID = await ClientID.getClientID();
|
this._clientID = await ClientID.getClientID();
|
||||||
} else if (!uploadEnabled && this._clientID != Utils.knownClientID) {
|
} else if (!uploadEnabled && this._clientID != Utils.knownClientID) {
|
||||||
this._log.trace(
|
this._log.trace(
|
||||||
"Upload disabled, but got a valid client ID. Setting canary client ID."
|
"Upload disabled, but got a valid client ID. Setting canary client ID."
|
||||||
);
|
);
|
||||||
await ClientID.setCanaryClientIDs();
|
await ClientID.setCanaryClientID();
|
||||||
this._clientID = await ClientID.getClientID();
|
this._clientID = await ClientID.getClientID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -904,7 +903,6 @@ var Impl = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TelemetryEventPing.startup();
|
TelemetryEventPing.startup();
|
||||||
EcosystemTelemetry.startup();
|
|
||||||
TelemetryPrioPing.startup();
|
TelemetryPrioPing.startup();
|
||||||
|
|
||||||
if (uploadEnabled) {
|
if (uploadEnabled) {
|
||||||
@ -962,8 +960,6 @@ var Impl = {
|
|||||||
|
|
||||||
this._shutdownStep = "Event" + now();
|
this._shutdownStep = "Event" + now();
|
||||||
TelemetryEventPing.shutdown();
|
TelemetryEventPing.shutdown();
|
||||||
this._shutdownStep = "Ecosystem" + now();
|
|
||||||
EcosystemTelemetry.shutdown();
|
|
||||||
this._shutdownStep = "Prio" + now();
|
this._shutdownStep = "Prio" + now();
|
||||||
await TelemetryPrioPing.shutdown();
|
await TelemetryPrioPing.shutdown();
|
||||||
|
|
||||||
@ -1118,7 +1114,7 @@ var Impl = {
|
|||||||
|
|
||||||
// Generate a new client ID and make sure this module uses the new version
|
// Generate a new client ID and make sure this module uses the new version
|
||||||
let p = (async () => {
|
let p = (async () => {
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
let id = await ClientID.getClientID();
|
let id = await ClientID.getClientID();
|
||||||
this._clientID = id;
|
this._clientID = id;
|
||||||
Telemetry.scalarSet("telemetry.data_upload_optin", true);
|
Telemetry.scalarSet("telemetry.data_upload_optin", true);
|
||||||
@ -1164,7 +1160,7 @@ var Impl = {
|
|||||||
|
|
||||||
// 6. Set ClientID to a known value
|
// 6. Set ClientID to a known value
|
||||||
let oldClientId = await ClientID.getClientID();
|
let oldClientId = await ClientID.getClientID();
|
||||||
await ClientID.setCanaryClientIDs();
|
await ClientID.setCanaryClientID();
|
||||||
this._clientID = await ClientID.getClientID();
|
this._clientID = await ClientID.getClientID();
|
||||||
|
|
||||||
// 7. Send the deletion-request ping.
|
// 7. Send the deletion-request ping.
|
||||||
|
@ -21,9 +21,6 @@ const { clearTimeout, setTimeout } = ChromeUtils.import(
|
|||||||
"resource://gre/modules/Timer.jsm"
|
"resource://gre/modules/Timer.jsm"
|
||||||
);
|
);
|
||||||
// Other pings
|
// Other pings
|
||||||
const { EcosystemTelemetry } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/EcosystemTelemetry.jsm"
|
|
||||||
);
|
|
||||||
const { TelemetryPrioPing } = ChromeUtils.import(
|
const { TelemetryPrioPing } = ChromeUtils.import(
|
||||||
"resource://gre/modules/PrioPing.jsm"
|
"resource://gre/modules/PrioPing.jsm"
|
||||||
);
|
);
|
||||||
@ -374,7 +371,6 @@ var TelemetryScheduler = {
|
|||||||
this._log.trace("_schedulerTickLogic - Periodic ping due.");
|
this._log.trace("_schedulerTickLogic - Periodic ping due.");
|
||||||
this._lastPeriodicPingTime = now;
|
this._lastPeriodicPingTime = now;
|
||||||
// Send other pings.
|
// Send other pings.
|
||||||
EcosystemTelemetry.periodicPing();
|
|
||||||
TelemetryPrioPing.periodicPing();
|
TelemetryPrioPing.periodicPing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,6 @@ var TelemetryUtils = {
|
|||||||
EventPingMinimumFrequency: "toolkit.telemetry.eventping.minimumFrequency",
|
EventPingMinimumFrequency: "toolkit.telemetry.eventping.minimumFrequency",
|
||||||
EventPingMaximumFrequency: "toolkit.telemetry.eventping.maximumFrequency",
|
EventPingMaximumFrequency: "toolkit.telemetry.eventping.maximumFrequency",
|
||||||
|
|
||||||
// Ecosystem Telemetry Preferences
|
|
||||||
EcosystemTelemetryEnabled: "toolkit.telemetry.ecosystemtelemetry.enabled",
|
|
||||||
EcosystemTelemetryAllowForNonProductionFxA:
|
|
||||||
"toolkit.telemetry.ecosystemtelemetry.allowForNonProductionFxA",
|
|
||||||
|
|
||||||
// Prio Ping Preferences
|
// Prio Ping Preferences
|
||||||
PrioPingEnabled: "toolkit.telemetry.prioping.enabled",
|
PrioPingEnabled: "toolkit.telemetry.prioping.enabled",
|
||||||
PrioPingDataLimit: "toolkit.telemetry.prioping.dataLimit",
|
PrioPingDataLimit: "toolkit.telemetry.prioping.dataLimit",
|
||||||
|
@ -148,20 +148,6 @@ Preferences
|
|||||||
The maximum frequency at which an :doc:`../data/event-ping` will be sent.
|
The maximum frequency at which an :doc:`../data/event-ping` will be sent.
|
||||||
Default is 10 (minutes).
|
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``
|
``toolkit.telemetry.overrideUpdateChannel``
|
||||||
|
|
||||||
Override the ``channel`` value that is reported via Telemetry.
|
Override the ``channel`` value that is reported via Telemetry.
|
||||||
|
@ -106,7 +106,6 @@ EXTRA_JS_MODULES += [
|
|||||||
"app/TelemetryTimestamps.jsm",
|
"app/TelemetryTimestamps.jsm",
|
||||||
"app/TelemetryUtils.jsm",
|
"app/TelemetryUtils.jsm",
|
||||||
"pings/CoveragePing.jsm",
|
"pings/CoveragePing.jsm",
|
||||||
"pings/EcosystemTelemetry.jsm",
|
|
||||||
"pings/EventPing.jsm",
|
"pings/EventPing.jsm",
|
||||||
"pings/HealthPing.jsm",
|
"pings/HealthPing.jsm",
|
||||||
"pings/ModulesPing.jsm",
|
"pings/ModulesPing.jsm",
|
||||||
|
@ -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();
|
|
||||||
},
|
|
||||||
};
|
|
@ -527,13 +527,6 @@ if (runningInParent) {
|
|||||||
// Speed up child process accumulations
|
// Speed up child process accumulations
|
||||||
Services.prefs.setIntPref(TelemetryUtils.Preferences.IPCBatchTimeout, 10);
|
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
|
// Non-unified Telemetry (e.g. Fennec on Android) needs the preference to be set
|
||||||
// in order to enable Telemetry.
|
// in order to enable Telemetry.
|
||||||
if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false)) {
|
if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false)) {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
);
|
|
@ -265,8 +265,6 @@ add_task(async function test_disableDataUpload() {
|
|||||||
secondClientId,
|
secondClientId,
|
||||||
"The client id must have changed"
|
"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.
|
// Simulate a failure in sending the deletion-request ping by disabling the HTTP server.
|
||||||
await PingServer.stop();
|
await PingServer.stop();
|
||||||
|
|
||||||
@ -338,15 +336,6 @@ add_task(async function test_disableDataUpload() {
|
|||||||
ping.clientId,
|
ping.clientId,
|
||||||
"Deletion must be requested for correct client id"
|
"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
|
// 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,
|
// to shut down telemetry, even though the PingServer caught the expected pings,
|
||||||
|
@ -11,9 +11,6 @@ const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
|||||||
|
|
||||||
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
|
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
|
||||||
|
|
||||||
const SCALAR_DELETION_REQUEST_ECOSYSTEM_CLIENT_ID =
|
|
||||||
"deletion.request.ecosystem_client_id";
|
|
||||||
|
|
||||||
var drsPath;
|
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;
|
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();
|
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() {
|
add_task(async function test_client_id() {
|
||||||
const invalidIDs = [
|
const invalidIDs = [
|
||||||
[-1, "setIntPref"],
|
[-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";
|
const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
|
||||||
|
|
||||||
await ClientID._reset();
|
await ClientID._reset();
|
||||||
|
|
||||||
// We should be able to set a valid UUID
|
// We should be able to set a valid UUID
|
||||||
await ClientID.setCanaryClientIDs();
|
await ClientID.setCanaryClientID();
|
||||||
let clientID = await ClientID.getClientID();
|
let clientID = await ClientID.getClientID();
|
||||||
Assert.equal(KNOWN_UUID, clientID);
|
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() {
|
add_task(async function test_removeParallelGet() {
|
||||||
// We should get a valid UUID after reset
|
// We should get a valid UUID after reset
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
let firstClientID = await ClientID.getClientID();
|
let firstClientID = await ClientID.getClientID();
|
||||||
|
|
||||||
// We should get the same ID twice when requesting it in parallel to a reset.
|
// 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 p = ClientID.getClientID();
|
||||||
let newClientID = await ClientID.getClientID();
|
let newClientID = await ClientID.getClientID();
|
||||||
await promiseRemoveClientIDs;
|
await promiseRemoveClientID;
|
||||||
let otherClientID = await p;
|
let otherClientID = await p;
|
||||||
|
|
||||||
Assert.notEqual(
|
Assert.notEqual(
|
||||||
@ -306,19 +182,19 @@ add_task(
|
|||||||
const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
|
const KNOWN_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
|
||||||
|
|
||||||
// We should get a valid UUID after reset
|
// We should get a valid UUID after reset
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
let firstClientID = await ClientID.getClientID();
|
let firstClientID = await ClientID.getClientID();
|
||||||
Assert.notEqual(KNOWN_UUID, firstClientID, "Client ID should be random.");
|
Assert.notEqual(KNOWN_UUID, firstClientID, "Client ID should be random.");
|
||||||
|
|
||||||
// Set the canary client ID.
|
// Set the canary client ID.
|
||||||
await ClientID.setCanaryClientIDs();
|
await ClientID.setCanaryClientID();
|
||||||
Assert.equal(
|
Assert.equal(
|
||||||
KNOWN_UUID,
|
KNOWN_UUID,
|
||||||
await ClientID.getClientID(),
|
await ClientID.getClientID(),
|
||||||
"Client ID should be known canary."
|
"Client ID should be known canary."
|
||||||
);
|
);
|
||||||
|
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
let newClientID = await ClientID.getClientID();
|
let newClientID = await ClientID.getClientID();
|
||||||
Assert.notEqual(
|
Assert.notEqual(
|
||||||
KNOWN_UUID,
|
KNOWN_UUID,
|
||||||
@ -335,7 +211,7 @@ add_task(
|
|||||||
"After reset we should have detected a canary client ID"
|
"After reset we should have detected a canary client ID"
|
||||||
);
|
);
|
||||||
|
|
||||||
await ClientID.removeClientIDs();
|
await ClientID.removeClientID();
|
||||||
let clientID = await ClientID.getClientID();
|
let clientID = await ClientID.getClientID();
|
||||||
Assert.notEqual(
|
Assert.notEqual(
|
||||||
KNOWN_UUID,
|
KNOWN_UUID,
|
||||||
|
@ -91,8 +91,6 @@ skip-if = (os == "android") || (os == "linux" && bits == 32)
|
|||||||
[test_TelemetryUtils.js]
|
[test_TelemetryUtils.js]
|
||||||
[test_ThirdPartyModulesPing.js]
|
[test_ThirdPartyModulesPing.js]
|
||||||
run-if = nightly_build && (os == 'win')
|
run-if = nightly_build && (os == 'win')
|
||||||
[test_EcosystemTelemetry.js]
|
|
||||||
skip-if = appname == "thunderbird"
|
|
||||||
[test_EventPing.js]
|
[test_EventPing.js]
|
||||||
tags = coverage
|
tags = coverage
|
||||||
[test_CoveragePing.js]
|
[test_CoveragePing.js]
|
||||||
|
Loading…
Reference in New Issue
Block a user