/* 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/. */ import { Log } from "resource://gre/modules/Log.sys.mjs"; import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; const LOGGER_NAME = "Toolkit.Telemetry"; const LOGGER_PREFIX = "ClientID::"; // Must match ID in TelemetryUtils const CANARY_CLIENT_ID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0"; const CANARY_PROFILE_GROUP_ID = "decafdec-afde-cafd-ecaf-decafdecafde"; const CANARY_USAGE_PROFILE_ID = "beefbeef-beef-beef-beef-beeefbeefbee"; const CANARY_USAGE_PROFILE_GROUP_ID = "b0bacafe-b0ba-cafe-b0ba-cafeb0bacafe"; const DRS_STATE_VERSION = 2; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { CommonUtils: "resource://services-common/utils.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "CryptoHash", () => { return Components.Constructor( "@mozilla.org/security/hash;1", "nsICryptoHash", "initWithString" ); }); ChromeUtils.defineLazyGetter(lazy, "gDatareportingPath", () => { return PathUtils.join( Services.dirsvc.get("ProfD", Ci.nsIFile).path, "datareporting" ); }); ChromeUtils.defineLazyGetter(lazy, "gStateFilePath", () => { return PathUtils.join(lazy.gDatareportingPath, "state.json"); }); const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID"; const PREF_CACHED_PROFILEGROUPID = "toolkit.telemetry.cachedProfileGroupID"; const PREF_CACHED_USAGE_PROFILEID = "datareporting.dau.cachedUsageProfileID"; const PREF_CACHED_USAGE_PROFILEGROUPID = "datareporting.dau.cachedUsageProfileGroupID"; /** * Checks if the string is a valid UUID (without braces). * * @param {String} id A string containing an ID. * @return {Boolean} True when the ID has valid format, or False otherwise. */ function isValidUUID(id) { const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; return UUID_REGEX.test(id); } export var ClientID = Object.freeze({ /** * This returns a promise resolving to the stable client ID we use for * data reporting. * * @return {Promise} The stable client ID. */ getClientID() { return ClientIDImpl.getClientID(); }, /** * This returns a promise resolving to the stable profile group ID we use for * data reporting. * * @return {Promise} The stable profile group ID. */ getProfileGroupID() { return ClientIDImpl.getProfileGroupID(); }, /** * This returns a promise resolving to the stable Usage Profile ID we use for * DAU reporting. * * @return {Promise} The stable Usage Profile ID. */ getUsageProfileID() { return ClientIDImpl.getUsageProfileID(); }, /** * This returns a promise resolving to the stable Usage Profile Group ID we * use for DAU reporting. * * @return {Promise} The stable Usage Profile Group ID. */ getUsageProfileGroupID() { return ClientIDImpl.getUsageProfileGroupID(); }, /** * Asynchronously updates the stable profile group ID we use for data * reporting. * * @param {String} id A string containing the profile group ID. * @return {Promise} Resolves when the ID is updated. */ setProfileGroupID(id) { return ClientIDImpl.setProfileGroupID(id); }, /** * Asynchronously updates the stable Usage Profile ID we use for DAU * reporting. * * @param {String} id A string containing the Usage Profile ID. * @return {Promise} Resolves when the ID is updated. */ setUsageProfileID(id) { return ClientIDImpl.setUsageProfileID(id); }, /** * Asynchronously updates the stable Usage Profile Group ID we use for DAU * reporting. * * @param {String} id A string containing the Usage Group Profile ID. * @return {Promise} Resolves when the ID is updated. */ setUsageProfileGroupID(id) { return ClientIDImpl.setUsageProfileGroupID(id); }, /** * Get the client id synchronously without hitting the disk. * This returns: * - the current on-disk client id if it was already loaded * - the client id that we cached into preferences (if any) * - null otherwise */ getCachedClientID() { return ClientIDImpl.getCachedClientID(); }, /** * Get the profile group ID synchronously without hitting the disk. * This returns: * - the current on-disk profile group ID if it was already loaded * - the profile group ID that we cached into preferences (if any) * - null otherwise */ getCachedProfileGroupID() { return ClientIDImpl.getCachedProfileGroupID(); }, /** * Get the Usage Profile ID synchronously without hitting the disk. * This returns: * - the current on-disk Usage Profile ID if it was already loaded * - the Usage Profile ID that we cached into preferences (if any) * - null otherwise */ getCachedUsageProfileID() { return ClientIDImpl.getCachedUsageProfileID(); }, /** * Get the Usage Profile Group ID synchronously without hitting the disk. * This returns: * - the current on-disk Usage Profile Group ID if it was already loaded * - the Usage Profile Group ID that we cached into preferences (if any) * - null otherwise */ getCachedUsageProfileGroupID() { return ClientIDImpl.getCachedUsageProfileGroupID(); }, async getClientIdHash() { return ClientIDImpl.getClientIdHash(); }, /** * Sets the client ID and profile group ID to the canary (known) identifiers, * writing them to disk and updating the cached version. * * Use `resetIdentifiers` to clear the existing identifiers and generate new, * random ones if required. * * @return {Promise} */ setCanaryIdentifiers() { return ClientIDImpl.setCanaryIdentifiers(); }, /** * Sets the Usage Profile ID and Usage Profile Group ID to the canary (known) * identifiers, writing them to disk and updating the cached version. * * Does not touch the client ID or profile group ID. * * Use `resetUsageProfileIdentifiers` to clear the existing identifiers * and generate new, random ones if required. * * @return {Promise} */ setCanaryUsageProfileIdentifiers() { return ClientIDImpl.setCanaryUsageProfileIdentifiers(); }, /** * Assigns new random values to client ID and profile group ID. Should only be * used if a reset is explicitly requested by the user. * * @return {Promise} */ resetIdentifiers() { return ClientIDImpl.resetIdentifiers(); }, /** * Assigns new random values to the Usage Profile ID and Usage Profile Group * ID. Should only be used if a reset is explicitly requested by the user. * * Does not touch the client ID or profile group ID. * * @return {Promise} */ resetUsageProfileIdentifiers() { return ClientIDImpl.resetUsageProfileIdentifiers(); }, /** * Only used for testing. Invalidates the cached client ID and profile group * ID so that they are read again from file, but doesn't remove the existing * IDs from disk. */ _reset() { return ClientIDImpl._reset(); }, }); var ClientIDImpl = { _clientID: null, _clientIDHash: null, _profileGroupID: null, _usageProfileID: null, _loadClientIdTask: null, _saveDataReportingStateTask: null, _resetIdentifiersTask: null, _resetUsageProfileIdentifierTask: null, _logger: null, _loadDataReportingState() { if (this._loadClientIdTask) { return this._loadClientIdTask; } this._loadClientIdTask = this._doLoadDataReportingState(); let clear = () => (this._loadClientIdTask = null); this._loadClientIdTask.then(clear, clear); return this._loadClientIdTask; }, /** * Load the client ID, profile group ID, Usage Profile ID, and Usage Profile * Group ID from the DataReporting Service state file. If any are missing, * we generate a new one. */ async _doLoadDataReportingState() { this._log.trace(`_doLoadDataReportingState`); // If there's a removal in progress, let's wait for it await this._resetIdentifiersTask; await this._resetUsageProfileIdentifierTask; // Try to load the client id from the DRS state file. let hasCurrentClientID = false; let hasCurrentProfileGroupID = false; let hasCurrentUsageProfileID = false; let hasCurrentUsageProfileGroupID = false; try { let state = await IOUtils.readJSON(lazy.gStateFilePath); if (state) { if (!("version" in state)) { // Old version, clear out any previously generated profile group ID. delete state.profileGroupID; } hasCurrentClientID = this.updateClientID(state.clientID); hasCurrentProfileGroupID = this.updateProfileGroupID( state.profileGroupID ); hasCurrentUsageProfileID = this.updateUsageProfileID( state.usageProfileID ); hasCurrentUsageProfileGroupID = this.updateUsageProfileGroupID( state.usageProfileGroupID ); if (!hasCurrentProfileGroupID && hasCurrentClientID) { // A pre-existing profile should be assigned the existing client ID. hasCurrentProfileGroupID = this.updateProfileGroupID(this._clientID); if (hasCurrentProfileGroupID) { this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; } } if ( hasCurrentClientID && hasCurrentProfileGroupID && hasCurrentUsageProfileID && hasCurrentUsageProfileGroupID ) { this._log.trace( `_doLoadDataReportingState: Client ID, Profile Group ID, Usage Profile ID, and Usage Profile Group ID loaded from state.` ); return { clientID: this._clientID, profileGroupID: this._profileGroupID, usageProfileID: this._usageProfileID, usageProfileGroupID: this._usageProfileGroupID, }; } } } catch (e) { // fall through to next option } // Absent or broken state file? Check prefs as last resort. if (!hasCurrentClientID) { const cachedID = this.getCachedClientID(); // Calling `updateClientID` with `null` logs an error, which breaks tests. if (cachedID) { hasCurrentClientID = this.updateClientID(cachedID); } } if (!hasCurrentProfileGroupID) { const cachedID = this.getCachedProfileGroupID(); // Calling `updateProfileGroupID` with `null` logs an error, which breaks tests. if (cachedID) { hasCurrentProfileGroupID = this.updateProfileGroupID(cachedID); } } if (!hasCurrentUsageProfileID) { const cachedID = this.getCachedUsageProfileID(); // Calling `updateUsageProfileID` with `null` logs an error, which breaks tests. if (cachedID) { hasCurrentUsageProfileID = this.updateUsageProfileID(cachedID); } } if (!hasCurrentUsageProfileGroupID) { const cachedID = this.getCachedUsageProfileGroupID(); // Calling `updateUsageProfileGroupID` with `null` logs an error, which breaks tests. if (cachedID) { hasCurrentUsageProfileGroupID = this.updateUsageProfileGroupID(cachedID); } } // We're missing the ID from the DRS state file and prefs. // Generate a new one. if (!hasCurrentClientID) { this.updateClientID(lazy.CommonUtils.generateUUID()); } if (!hasCurrentProfileGroupID) { this.updateProfileGroupID(lazy.CommonUtils.generateUUID()); } if (!hasCurrentUsageProfileID) { this.updateUsageProfileID(lazy.CommonUtils.generateUUID()); } if (!hasCurrentUsageProfileGroupID) { this.updateUsageProfileGroupID(lazy.CommonUtils.generateUUID()); } this._saveDataReportingStateTask = this._saveDataReportingState(); // 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._saveDataReportingStateTask; this._log.trace( "_doLoadDataReportingState: client ID, profile group ID and Usage Profile ID loaded and persisted." ); return { clientID: this._clientID, profileGroupID: this._profileGroupID, usageProfileID: this._usageProfileID, usageProfileGroupID: this._usageProfileGroupID, }; }, /** * Save the client ID, profile group ID, Usage Profile ID, and Usage Profile Group ID to the DRS state file. * * @return {Promise} A promise resolved when the DRS state file is saved to disk. */ async _saveDataReportingState() { try { this._log.trace(`_saveDataReportingState`); let obj = { version: DRS_STATE_VERSION, clientID: this._clientID, profileGroupID: this._profileGroupID, usageProfileID: this._usageProfileID, usageProfileGroupID: this._usageProfileGroupID, }; await IOUtils.makeDirectory(lazy.gDatareportingPath); await IOUtils.writeJSON(lazy.gStateFilePath, obj, { tmpPath: `${lazy.gStateFilePath}.tmp`, }); this._saveDataReportingStateTask = null; } catch (ex) { if (!DOMException.isInstance(ex) || ex.name !== "AbortError") { throw ex; } } }, /** * This returns a promise resolving to the stable client ID we use for * data reporting (FHR & Telemetry). * * @return {Promise} The stable client ID. */ async getClientID() { if (!this._clientID) { let { clientID } = await this._loadDataReportingState(); if (AppConstants.platform != "android") { Glean.legacyTelemetry.clientId.set(clientID); } return clientID; } return Promise.resolve(this._clientID); }, /** * This returns a promise resolving to the stable profile group ID we use for * data reporting (FHR & Telemetry). * * @return {Promise} The stable profile group ID. */ async getProfileGroupID() { if (!this._profileGroupID) { let { profileGroupID } = await this._loadDataReportingState(); if (AppConstants.platform != "android") { Glean.legacyTelemetry.profileGroupId.set(profileGroupID); } return profileGroupID; } return this._profileGroupID; }, /** * This returns a promise resolving to the stable Usage Profile ID we use for * DAU reporting. * * @return {Promise} The stable Usage Profile ID. */ async getUsageProfileID() { if (!this._usageProfileID) { let { usageProfileID } = await this._loadDataReportingState(); if (AppConstants.platform != "android") { Glean.usage.profileId.set(usageProfileID); } return usageProfileID; } return this._usageProfileID; }, /** * This returns a promise resolving to the stable Usage Profile Group ID we * use for DAU reporting. * * @return {Promise} The stable Usage Profile Group ID. */ async getUsageProfileGroupID() { if (!this._usageProfileGroupID) { let { usageProfileGroupID } = await this._loadDataReportingState(); if (AppConstants.platform != "android") { Glean.usage.profileGroupId.set(usageProfileGroupID); } return usageProfileGroupID; } return this._usageProfileGroupID; }, /** * Asynchronously updates the stable profile group ID we use for data reporting * (FHR & Telemetry). * * @param {String} id A string containing the profile group ID. * @return {Promise} Resolves when the ID is updated. */ async setProfileGroupID(id) { // Make sure that we have loaded the client ID. Do this before updating the // profile group ID as loading would clobber that. if (!this._clientID) { await this._loadDataReportingState(); } if (!(await this.updateProfileGroupID(id))) { this._log.error( "setProfileGroupID - invalid profile group ID passed, not updating" ); throw new Error("Invalid profile group ID"); } // If there is already a save in progress, wait for it to complete. await this._saveDataReportingStateTask; this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; }, /** * Asynchronously updates the stable Usage Profile ID we use for DAU reporting. * * @param {String} id A string containing the Usage Profile ID. * @return {Promise} Resolves when the ID is updated. */ async setUsageProfileID(id) { // Make sure that we have loaded the client ID. Do this before updating the // Usage Profile ID as loading would clobber that. if (!this._clientID) { await this._loadDataReportingState(); } GleanPings.usageReporting.setEnabled(true); if (!(await this.updateUsageProfileID(id))) { this._log.error( "setUsageProfileID - invalid Usage Profile ID passed, not updating" ); throw new Error("Invalid Usage Profile ID"); } // If there is already a save in progress, wait for it to complete. await this._saveDataReportingStateTask; this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; }, /** * Asynchronously updates the stable Usage Profile Group ID we use for DAU * reporting. * * @param {String} id A string containing the Usage Profile Group ID. * @return {Promise} Resolves when the ID is updated. */ async setUsageProfileGroupID(id) { // Make sure that we have loaded the client ID. Do this before updating the // Usage Profile Group ID as loading would clobber that. if (!this._clientID) { await this._loadDataReportingState(); } if (!(await this.updateUsageProfileGroupID(id))) { this._log.error( "setUsageProfileGroupID - invalid Usage Profile Group ID passed, not updating" ); throw new Error("Invalid Usage Profile Group ID"); } // If there is already a save in progress, wait for it to complete. await this._saveDataReportingStateTask; this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; }, /** * Get the client id synchronously without hitting the disk. * This returns: * - the current on-disk client id if it was already loaded * - the client id that we cached into preferences (if any) * - null otherwise */ getCachedClientID() { if (this._clientID) { // Already loaded the client id from disk. return this._clientID; } // If the client id cache contains a value of the wrong type, // reset the pref. We need to do this before |getStringPref| since // it will just return |null| in that case and we won't be able // to distinguish between the missing pref and wrong type cases. if ( Services.prefs.prefHasUserValue(PREF_CACHED_CLIENTID) && Services.prefs.getPrefType(PREF_CACHED_CLIENTID) != Ci.nsIPrefBranch.PREF_STRING ) { this._log.error( "getCachedClientID - invalid client id type in preferences, resetting" ); Services.prefs.clearUserPref(PREF_CACHED_CLIENTID); } // Not yet loaded, return the cached client id if we have one. let id = Services.prefs.getStringPref(PREF_CACHED_CLIENTID, null); if (id === null) { return null; } if (!isValidUUID(id)) { this._log.error( "getCachedClientID - invalid client id in preferences, resetting", id ); Services.prefs.clearUserPref(PREF_CACHED_CLIENTID); return null; } return id; }, /** * Get the profile group ID synchronously without hitting the disk. * This returns: * - the current on-disk profile group ID if it was already loaded * - the profile group ID that we cached into preferences (if any) * - null otherwise */ getCachedProfileGroupID() { if (this._profileGroupID) { // Already loaded the profile group ID from disk. return this._profileGroupID; } // If the profile group ID cache contains a value of the wrong type, // reset the pref. We need to do this before |getStringPref| since // it will just return |null| in that case and we won't be able // to distinguish between the missing pref and wrong type cases. if ( Services.prefs.prefHasUserValue(PREF_CACHED_PROFILEGROUPID) && Services.prefs.getPrefType(PREF_CACHED_PROFILEGROUPID) != Ci.nsIPrefBranch.PREF_STRING ) { this._log.error( "getCachedProfileGroupID - invalid profile group ID type in preferences, resetting" ); Services.prefs.clearUserPref(PREF_CACHED_PROFILEGROUPID); } // Not yet loaded, return the cached profile group ID if we have one. let id = Services.prefs.getStringPref(PREF_CACHED_PROFILEGROUPID, null); if (id === null) { return null; } if (!isValidUUID(id)) { this._log.error( "getCachedProfileGroupID - invalid profile group ID in preferences, resetting", id ); Services.prefs.clearUserPref(PREF_CACHED_PROFILEGROUPID); return null; } return id; }, /** * Get the Usage Profile ID synchronously without hitting the disk. * This returns: * - the current on-disk Usage Profile ID if it was already loaded * - the Usage Profile ID that we cached into preferences (if any) * - null otherwise */ getCachedUsageProfileID() { if (this._usageProfileID) { // Already loaded the Usage Profile ID from disk. return this._usageProfileID; } // If the Usage Profile ID cache contains a value of the wrong type, // reset the pref. We need to do this before |getStringPref| since // it will just return |null| in that case and we won't be able // to distinguish between the missing pref and wrong type cases. if ( Services.prefs.prefHasUserValue(PREF_CACHED_USAGE_PROFILEID) && Services.prefs.getPrefType(PREF_CACHED_USAGE_PROFILEID) != Ci.nsIPrefBranch.PREF_STRING ) { this._log.error( "getCachedUsageProfileID - invalid Usage Profile ID type in preferences, resetting" ); Services.prefs.clearUserPref(PREF_CACHED_USAGE_PROFILEID); } // Not yet loaded, return the cached Usage Profile ID if we have one. let id = Services.prefs.getStringPref(PREF_CACHED_USAGE_PROFILEID, null); if (id === null) { return null; } if (!isValidUUID(id)) { this._log.error( "getCachedUsageProfileID - invalid Usage Profile ID in preferences, resetting", id ); Services.prefs.clearUserPref(PREF_CACHED_USAGE_PROFILEID); return null; } return id; }, /** * Get the Usage Profile Group ID synchronously without hitting the disk. * This returns: * - the current on-disk Usage Profile Group ID if it was already loaded * - the Usage Profile Group ID that we cached into preferences (if any) * - null otherwise */ getCachedUsageProfileGroupID() { if (this._usageProfileGroupID) { // Already loaded the Usage Profile Group ID from disk. return this._usageProfileGroupID; } // If the Usage Profile Group ID cache contains a value of the wrong type, // reset the pref. We need to do this before |getStringPref| since // it will just return |null| in that case and we won't be able // to distinguish between the missing pref and wrong type cases. if ( Services.prefs.prefHasUserValue(PREF_CACHED_USAGE_PROFILEGROUPID) && Services.prefs.getPrefType(PREF_CACHED_USAGE_PROFILEGROUPID) != Ci.nsIPrefBranch.PREF_STRING ) { this._log.error( "getCachedUsageProfileGroupID - invalid Usage Profile Group ID type in preferences, resetting" ); Services.prefs.clearUserPref(PREF_CACHED_USAGE_PROFILEGROUPID); } // Not yet loaded, return the cached Usage Profile Group ID if we have one. let id = Services.prefs.getStringPref( PREF_CACHED_USAGE_PROFILEGROUPID, null ); if (id === null) { return null; } if (!isValidUUID(id)) { this._log.error( "getCachedUsageProfileGroupID - invalid Usage Profile Group ID type in preferences, resetting", id ); Services.prefs.clearUserPref(PREF_CACHED_USAGE_PROFILEGROUPID); return null; } return id; }, async getClientIdHash() { if (!this._clientIDHash) { let byteArr = new TextEncoder().encode(await this.getClientID()); let hash = new lazy.CryptoHash("sha256"); hash.update(byteArr, byteArr.length); this._clientIDHash = lazy.CommonUtils.bytesAsHex(hash.finish(false)); } return this._clientIDHash; }, /** * Resets the module. This is for testing only. */ async _reset() { await this._loadClientIdTask; await this._saveDataReportingStateTask; this._clientID = null; this._clientIDHash = null; this._profileGroupID = null; this._usageProfileID = null; this._usageProfileGroupID = null; }, async setCanaryIdentifiers() { this._log.trace("setCanaryIdentifiers"); this.updateClientID(CANARY_CLIENT_ID); this.updateProfileGroupID(CANARY_PROFILE_GROUP_ID); this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; return this._clientID; }, async setCanaryUsageProfileIdentifiers() { this._log.trace("setCanaryUsageProfileIdentifiers"); this.updateUsageProfileID(CANARY_USAGE_PROFILE_ID); this.updateUsageProfileGroupID(CANARY_USAGE_PROFILE_GROUP_ID); GleanPings.usageReporting.setEnabled(false); this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; return this._usageProfileID; }, async _doResetIdentifiers() { this._log.trace("_doResetIdentifiers"); // Reset the cached client ID. this.updateClientID(lazy.CommonUtils.generateUUID()); this._clientIDHash = null; // Reset the cached profile group ID. this.updateProfileGroupID(lazy.CommonUtils.generateUUID()); // If there is a save in progress, wait for it to complete. await this._saveDataReportingStateTask; // Save the new identifiers to disk. this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; }, async _doResetUsageProfileIdentifiers() { this._log.trace("_doResetUsageProfileIdentifiers"); // Reset the cached Usage Profile ID. GleanPings.usageReporting.setEnabled(true); this.updateUsageProfileID(lazy.CommonUtils.generateUUID()); // Reset the cached Usage Profile Group ID. this.updateUsageProfileGroupID(lazy.CommonUtils.generateUUID()); // If there is a save in progress, wait for it to complete. await this._saveDataReportingStateTask; // Save the new identifiers to disk. this._saveDataReportingStateTask = this._saveDataReportingState(); await this._saveDataReportingStateTask; }, async resetIdentifiers() { this._log.trace("resetIdentifiers"); // Wait for the removal. // Asynchronous calls to getClientID will also be blocked on this. this._resetIdentifiersTask = this._doResetIdentifiers(); let clear = () => (this._resetIdentifiersTask = null); this._resetIdentifiersTask.then(clear, clear); await this._resetIdentifiersTask; }, async resetUsageProfileIdentifiers() { this._log.trace("resetUsageProfileIdentifiers"); // Wait for the removal. // Asynchronous calls to getClientID will also be blocked on this. this._resetUsageProfileIdentifierTask = this._doResetUsageProfileIdentifiers(); let clear = () => (this._resetUsageProfileIdentifierTask = null); this._resetUsageProfileIdentifierTask.then(clear, clear); await this._resetUsageProfileIdentifierTask; }, /** * Sets the client id to the given value and updates the value cached in * preferences only if the given id is a valid UUID. * * @param {String} id A string containing the client ID. * @return {Boolean} True when the client ID has valid format, or False * otherwise. */ updateClientID(id) { if (!isValidUUID(id)) { this._log.error("updateClientID - invalid client ID", id); return false; } this._clientID = id; if (AppConstants.platform != "android") { Glean.legacyTelemetry.clientId.set(id); } this._clientIDHash = null; Services.prefs.setStringPref(PREF_CACHED_CLIENTID, this._clientID); return true; }, /** * Sets the profile group ID to the given value and updates the value cached * in preferences only if the given id is a valid UUID. * * @param {String} id A string containing the profile group ID. * @return {Boolean} True when the profile group ID has valid format, or False * otherwise. */ updateProfileGroupID(id) { if (!isValidUUID(id)) { this._log.error("updateProfileGroupID - invalid profile group ID", id); return false; } this._profileGroupID = id; if (AppConstants.platform != "android") { Glean.legacyTelemetry.profileGroupId.set(id); } Services.prefs.setStringPref( PREF_CACHED_PROFILEGROUPID, this._profileGroupID ); return true; }, /** * Sets the Usage Profile ID to the given value and updates the value cached * in preferences only if the given id is a valid UUID. * * @param {String} id A string containing the Profile Usage ID. * @return {Boolean} True when the Profile Usage ID has valid format, or False * otherwise. */ updateUsageProfileID(id) { if (!isValidUUID(id)) { this._log.error("updateUsageProfileID - invalid Usage Profile ID", id); return false; } this._usageProfileID = id; if (AppConstants.platform != "android") { Glean.usage.profileId.set(id); } Services.prefs.setStringPref( PREF_CACHED_USAGE_PROFILEID, this._usageProfileID ); return true; }, /** * Sets the Usage Profile Group ID to the given value and updates the value * cached in preferences only if the given id is a valid UUID. * * @param {String} id A string containing the Profile Group Usage ID. * @return {Boolean} True when the Profile Group Usage ID has valid format, * or False otherwise. */ updateUsageProfileGroupID(id) { if (!isValidUUID(id)) { this._log.error( "updateUsageProfileGroupID - invalid Usage Profile Group ID", id ); return false; } this._usageProfileGroupID = id; if (AppConstants.platform != "android") { Glean.usage.profileGroupId.set(id); } Services.prefs.setStringPref( PREF_CACHED_USAGE_PROFILEGROUPID, this._usageProfileGroupID ); return true; }, /** * A helper for getting access to telemetry logger. */ get _log() { if (!this._logger) { this._logger = Log.repository.getLoggerWithMessagePrefix( LOGGER_NAME, LOGGER_PREFIX ); } return this._logger; }, };