Bug 1357517 - Remove Preferences.jsm usage from some Telemetry files. r=Dexter
authorMarco Castelluccio <mcastelluccio@mozilla.com>
Mon, 31 Jul 2017 01:32:31 +0200
changeset 372066 b1a2f780f9665c339fb4cf791ddc000a7099146f
parent 372065 06d31a972d445d5abcad85d9201aad70ec4962f6
child 372067 433a0c6cef710274dc82d1f2b30cb378d53b3b6e
push id32264
push userkwierso@gmail.com
push dateTue, 01 Aug 2017 00:43:47 +0000
treeherdermozilla-central@44121dbcac6a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersDexter
bugs1357517
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1357517 - Remove Preferences.jsm usage from some Telemetry files. r=Dexter
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetryEnvironment.jsm
toolkit/components/telemetry/TelemetryReportingPolicy.jsm
toolkit/components/telemetry/TelemetrySend.jsm
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/TelemetryUtils.jsm
toolkit/components/telemetry/UpdatePing.jsm
toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -11,36 +11,35 @@ const Cr = Components.results;
 const Cu = Components.utils;
 const myScope = this;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
 Cu.import("resource://gre/modules/DeferredTask.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 const Utils = TelemetryUtils;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetryController::";
 
 const PREF_BRANCH_LOG = "toolkit.telemetry.log.";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
-const IS_UNIFIED_TELEMETRY = Preferences.get(TelemetryUtils.Preferences.Unified, false);
+const IS_UNIFIED_TELEMETRY = Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false);
 
 const PING_FORMAT_VERSION = 4;
 
 // Delay before intializing telemetry (ms)
-const TELEMETRY_DELAY = Preferences.get("toolkit.telemetry.initDelay", 60) * 1000;
+const TELEMETRY_DELAY = Services.prefs.getIntPref("toolkit.telemetry.initDelay", 60) * 1000;
 // Delay before initializing telemetry if we're testing (ms)
 const TELEMETRY_TEST_DELAY = 1;
 
 // How long to wait (ms) before sending the new profile ping on the first
 // run of a new profile.
 const NEWPROFILE_PING_DEFAULT_DELAY = 30 * 60 * 1000;
 
 // Ping types.
@@ -90,24 +89,24 @@ var gLogAppenderDump = null;
 function configureLogging() {
   if (!gLogger) {
     gLogger = Log.repository.getLogger(LOGGER_NAME);
 
     // Log messages need to go to the browser console.
     let consoleAppender = new Log.ConsoleAppender(new Log.BasicFormatter());
     gLogger.addAppender(consoleAppender);
 
-    Preferences.observe(PREF_BRANCH_LOG, configureLogging);
+    Services.prefs.addObserver(PREF_BRANCH_LOG, configureLogging);
   }
 
   // Make sure the logger keeps up with the logging level preference.
-  gLogger.level = Log.Level[Preferences.get(TelemetryUtils.Preferences.LogLevel, "Warn")];
+  gLogger.level = Log.Level[Services.prefs.getStringPref(TelemetryUtils.Preferences.LogLevel, "Warn")];
 
   // If enabled in the preferences, add a dump appender.
-  let logDumping = Preferences.get(TelemetryUtils.Preferences.LogDump, false);
+  let logDumping = Services.prefs.getBoolPref(TelemetryUtils.Preferences.LogDump, false);
   if (logDumping != !!gLogAppenderDump) {
     if (logDumping) {
       gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
       gLogger.addAppender(gLogAppenderDump);
     } else {
       gLogger.removeAppender(gLogAppenderDump);
       gLogAppenderDump = null;
     }
@@ -716,17 +715,17 @@ var Impl = {
         // Load the ClientID.
         this._clientID = await ClientID.getClientID();
 
         await TelemetrySend.setup(this._testMode);
 
         // Perform TelemetrySession delayed init.
         await TelemetrySession.delayedInit();
 
-        if (Preferences.get(TelemetryUtils.Preferences.NewProfilePingEnabled, false) &&
+        if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.NewProfilePingEnabled, false) &&
             !TelemetrySession.newProfilePingSent) {
           // Kick off the scheduling of the new-profile ping.
           this.scheduleNewProfilePing();
         }
 
         // Purge the pings archive by removing outdated pings. We don't wait for
         // this task to complete, but TelemetryStorage blocks on it during
         // shutdown.
@@ -775,17 +774,17 @@ var Impl = {
   // Do proper shutdown waiting and cleanup.
   async _cleanupOnShutdown() {
     if (!this._initialized) {
       return;
     }
 
     this._shuttingDown = true;
 
-    Preferences.ignore(PREF_BRANCH_LOG, configureLogging);
+    Services.prefs.removeObserver(PREF_BRANCH_LOG, configureLogging);
     this._detachObservers();
 
     // Now do an orderly shutdown.
     try {
       if (this._delayedNewPingTask) {
         await this._delayedNewPingTask.finalize();
       }
 
@@ -861,16 +860,20 @@ var Impl = {
 
     switch (aTopic) {
     case "profile-after-change":
       // profile-after-change is only registered for chrome processes.
       return this.setupTelemetry();
     case "app-startup":
       // app-startup is only registered for content processes.
       return this.setupContentTelemetry();
+    case "nsPref:changed":
+      if (aData == TelemetryUtils.Preferences.FhrUploadEnabled) {
+        return this._onUploadPrefChange();
+      }
     }
     return undefined;
   },
 
   /**
    * Get an object describing the current state of this module for AsyncShutdown diagnostics.
    */
   _getState() {
@@ -885,17 +888,17 @@ var Impl = {
     };
   },
 
   /**
    * Called whenever the FHR Upload preference changes (e.g. when user disables FHR from
    * the preferences panel), this triggers sending the deletion ping.
    */
   _onUploadPrefChange() {
-    const uploadEnabled = Preferences.get(TelemetryUtils.Preferences.FhrUploadEnabled, false);
+    const uploadEnabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, false);
     if (uploadEnabled) {
       // There's nothing we should do if we are enabling upload.
       return;
     }
 
     let p = (async () => {
       try {
         // Clear the current pings.
@@ -911,29 +914,31 @@ var Impl = {
         this.submitExternalPing(PING_TYPE_DELETION, {}, { addClientId: true });
       }
     })();
 
     this._shutdownBarrier.client.addBlocker(
       "TelemetryController: removing pending pings after data upload was disabled", p);
   },
 
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+
   _attachObservers() {
     if (IS_UNIFIED_TELEMETRY) {
       // Watch the FHR upload setting to trigger deletion pings.
-      Preferences.observe(TelemetryUtils.Preferences.FhrUploadEnabled, this._onUploadPrefChange, this);
+      Services.prefs.addObserver(TelemetryUtils.Preferences.FhrUploadEnabled, this, true);
     }
   },
 
   /**
    * Remove the preference observer to avoid leaks.
    */
   _detachObservers() {
     if (IS_UNIFIED_TELEMETRY) {
-      Preferences.ignore(TelemetryUtils.Preferences.FhrUploadEnabled, this._onUploadPrefChange, this);
+      Services.prefs.removeObserver(TelemetryUtils.Preferences.FhrUploadEnabled, this);
     }
   },
 
   /**
    * Allows waiting for TelemetryControllers delayed initialization to complete.
    * This will complete before TelemetryController is shutting down.
    * @return {Promise} Resolved when delayed TelemetryController initialization completed.
    */
@@ -985,17 +990,17 @@ var Impl = {
 
   /**
    * Schedule sending the "new-profile" ping.
    */
   scheduleNewProfilePing() {
     this._log.trace("scheduleNewProfilePing");
 
     const sendDelay =
-      Preferences.get(TelemetryUtils.Preferences.NewProfilePingDelay, NEWPROFILE_PING_DEFAULT_DELAY);
+      Services.prefs.getIntPref(TelemetryUtils.Preferences.NewProfilePingDelay, NEWPROFILE_PING_DEFAULT_DELAY);
 
     this._delayedNewPingTask = new DeferredTask(async () => {
       try {
         await this.sendNewProfilePing();
       } finally {
         this._delayedNewPingTask = null;
       }
     }, sendDelay);
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -8,17 +8,16 @@ this.EXPORTED_SYMBOLS = [
   "TelemetryEnvironment",
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 const myScope = this;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/ObjectUtils.jsm");
 Cu.import("resource://gre/modules/TelemetryController.jsm", this);
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 const Utils = TelemetryUtils;
 
@@ -253,16 +252,17 @@ const PREF_E10S_COHORT = "e10s.rollout.c
 const COMPOSITOR_CREATED_TOPIC = "compositor:created";
 const COMPOSITOR_PROCESS_ABORTED_TOPIC = "compositor:process-aborted";
 const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = "distribution-customization-complete";
 const EXPERIMENTS_CHANGED_TOPIC = "experiments-changed";
 const GFX_FEATURES_READY_TOPIC = "gfx-features-ready";
 const SEARCH_ENGINE_MODIFIED_TOPIC = "browser-search-engine-modified";
 const SEARCH_SERVICE_TOPIC = "browser-search-service";
 const SESSIONSTORE_WINDOWS_RESTORED_TOPIC = "sessionstore-windows-restored";
+const PREF_CHANGED_TOPIC = "nsPref:changed";
 
 /**
  * Enforces the parameter to a boolean value.
  * @param aValue The input value.
  * @return {Boolean|Object} If aValue is a boolean or a number, returns its truthfulness
  *         value. Otherwise, return null.
  */
 function enforceBoolean(aValue) {
@@ -994,48 +994,60 @@ EnvironmentCache.prototype = {
    * Get an object containing the values for the watched preferences. Depending on the
    * policy, the value for a preference or whether it was changed by user is reported.
    *
    * @return An object containing the preferences values.
    */
   _getPrefData() {
     let prefData = {};
     for (let [pref, policy] of this._watchedPrefs.entries()) {
+      let prefType = Services.prefs.getPrefType(pref);
+
       if (policy.what == TelemetryEnvironment.RECORD_DEFAULTPREF_VALUE) {
         // For default prefs, make sure they exist
-        if (!Preferences.has(pref)) {
+        if (prefType == Ci.nsIPrefBranch.PREF_INVALID) {
           continue;
         }
-      } else if (!Preferences.isSet(pref)) {
+      } else if (!Services.prefs.prefHasUserValue(pref)) {
         // For user prefs, make sure they are set
         continue;
       }
 
       // Check the policy for the preference and decide if we need to store its value
       // or whether it changed from the default value.
-      let prefValue = undefined;
+      let prefValue;
       if (policy.what == TelemetryEnvironment.RECORD_PREF_STATE) {
         prefValue = "<user-set>";
+      } else if (prefType == Ci.nsIPrefBranch.PREF_STRING) {
+        prefValue = Services.prefs.getStringPref(pref);
+      } else if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
+        prefValue = Services.prefs.getBoolPref(pref);
+      } else if (prefType == Ci.nsIPrefBranch.PREF_INT) {
+        prefValue = Services.prefs.getIntPref(pref);
+      } else if (prefType == Ci.nsIPrefBranch.PREF_INVALID) {
+        prefValue = null;
       } else {
-        prefValue = Preferences.get(pref, null);
+        throw new Error(`Unexpected preference type ("${prefType}") for "${pref}".`);
       }
       prefData[pref] = prefValue;
     }
     return prefData;
   },
 
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+
   /**
    * Start watching the preferences.
    */
   _startWatchingPrefs() {
     this._log.trace("_startWatchingPrefs - " + this._watchedPrefs);
 
     for (let [pref, options] of this._watchedPrefs) {
       if (!("requiresRestart" in options) || !options.requiresRestart) {
-        Preferences.observe(pref, this._onPrefChanged, this);
+        Services.prefs.addObserver(pref, this, true);
       }
     }
   },
 
   _onPrefChanged() {
     this._log.trace("_onPrefChanged");
     let oldEnvironment = Cu.cloneInto(this._currentEnvironment, myScope);
     this._updateSettings();
@@ -1045,17 +1057,17 @@ EnvironmentCache.prototype = {
   /**
    * Do not receive any more change notifications for the preferences.
    */
   _stopWatchingPrefs() {
     this._log.trace("_stopWatchingPrefs");
 
     for (let [pref, options] of this._watchedPrefs) {
       if (!("requiresRestart" in options) || !options.requiresRestart) {
-        Preferences.ignore(pref, this._onPrefChanged, this);
+        Services.prefs.removeObserver(pref, this);
       }
     }
   },
 
   _addObservers() {
     // Watch the search engine change and service topics.
     Services.obs.addObserver(this, SESSIONSTORE_WINDOWS_RESTORED_TOPIC);
     Services.obs.addObserver(this, COMPOSITOR_CREATED_TOPIC);
@@ -1118,16 +1130,21 @@ EnvironmentCache.prototype = {
         this._sessionWasRestored = true;
         // Make sure to initialize the search service once we've done restoring
         // the windows, so that we don't risk loosing search data.
         Services.search.init();
         // The default browser check could take some time, so just call it after
         // the session was restored.
         this._updateDefaultBrowser();
         break;
+      case PREF_CHANGED_TOPIC:
+        if (this._watchedPrefs.has(aData)) {
+          this._onPrefChanged();
+        }
+        break;
     }
   },
 
   /**
    * Get the default search engine.
    * @return {String} Returns the search engine identifier, "NONE" if no default search
    *         engine is defined or "UNDEFINED" if no engine identifier or name can be found.
    */
@@ -1240,17 +1257,17 @@ EnvironmentCache.prototype = {
       applicationId: Services.appinfo.ID || null,
       applicationName: Services.appinfo.name || null,
       architecture: Services.sysinfo.get("arch"),
       buildId: Services.appinfo.appBuildID || null,
       version: Services.appinfo.version || null,
       vendor: Services.appinfo.vendor || null,
       platformVersion: Services.appinfo.platformVersion || null,
       xpcomAbi: Services.appinfo.XPCOMABI,
-      hotfixVersion: Preferences.get(PREF_HOTFIX_LASTVERSION, null),
+      hotfixVersion: Services.prefs.getStringPref(PREF_HOTFIX_LASTVERSION, null),
     };
 
     // Add |architecturesInBinary| only for Mac Universal builds.
     if ("@mozilla.org/xpcom/mac-utils;1" in Cc) {
       let macUtils = Cc["@mozilla.org/xpcom/mac-utils;1"].getService(Ci.nsIMacUtils);
       if (macUtils && macUtils.isUniversalBinary) {
         buildData.architecturesInBinary = macUtils.architecturesInBinary;
       }
@@ -1312,26 +1329,26 @@ EnvironmentCache.prototype = {
    */
   _updateSettings() {
     let updateChannel = null;
     try {
       updateChannel = UpdateUtils.getUpdateChannel(false);
     } catch (e) {}
 
     this._currentEnvironment.settings = {
-      blocklistEnabled: Preferences.get(PREF_BLOCKLIST_ENABLED, true),
+      blocklistEnabled: Services.prefs.getBoolPref(PREF_BLOCKLIST_ENABLED, true),
       e10sEnabled: Services.appinfo.browserTabsRemoteAutostart,
       e10sMultiProcesses: Services.appinfo.maxWebProcessCount,
-      e10sCohort: Preferences.get(PREF_E10S_COHORT, "unknown"),
+      e10sCohort: Services.prefs.getStringPref(PREF_E10S_COHORT, "unknown"),
       telemetryEnabled: Utils.isTelemetryEnabled,
       locale: getBrowserLocale(),
       update: {
         channel: updateChannel,
-        enabled: Preferences.get(PREF_UPDATE_ENABLED, true),
-        autoDownload: Preferences.get(PREF_UPDATE_AUTODOWNLOAD, true),
+        enabled: Services.prefs.getBoolPref(PREF_UPDATE_ENABLED, true),
+        autoDownload: Services.prefs.getBoolPref(PREF_UPDATE_AUTODOWNLOAD, true),
       },
       userPrefs: this._getPrefData(),
       sandbox: this._getSandboxData(),
     };
 
     this._currentEnvironment.settings.addonCompatibilityCheckEnabled =
       AddonManager.checkCompatibility;
 
@@ -1387,21 +1404,21 @@ EnvironmentCache.prototype = {
   },
 
   /**
    * Get the partner data in object form.
    * @return Object containing the partner data.
    */
   _getPartner() {
     let partnerData = {
-      distributionId: Preferences.get(PREF_DISTRIBUTION_ID, null),
-      distributionVersion: Preferences.get(PREF_DISTRIBUTION_VERSION, null),
-      partnerId: Preferences.get(PREF_PARTNER_ID, null),
-      distributor: Preferences.get(PREF_DISTRIBUTOR, null),
-      distributorChannel: Preferences.get(PREF_DISTRIBUTOR_CHANNEL, null),
+      distributionId: Services.prefs.getStringPref(PREF_DISTRIBUTION_ID, null),
+      distributionVersion: Services.prefs.getStringPref(PREF_DISTRIBUTION_VERSION, null),
+      partnerId: Services.prefs.getStringPref(PREF_PARTNER_ID, null),
+      distributor: Services.prefs.getStringPref(PREF_DISTRIBUTOR, null),
+      distributorChannel: Services.prefs.getStringPref(PREF_DISTRIBUTOR_CHANNEL, null),
     };
 
     // Get the PREF_APP_PARTNER_BRANCH branch and append its children to partner data.
     let partnerBranch = Services.prefs.getBranch(PREF_APP_PARTNER_BRANCH);
     partnerData.partnerNames = partnerBranch.getChildList("");
 
     return partnerData;
   },
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -6,17 +6,16 @@
 
 this.EXPORTED_SYMBOLS = [
   "TelemetryReportingPolicy"
 ];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://services-common/observers.js", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
@@ -181,17 +180,17 @@ var TelemetryReportingPolicyImpl = {
     return this._logger;
   },
 
   /**
    * Get the date the policy was notified.
    * @return {Object} A date object or null on errors.
    */
   get dataSubmissionPolicyNotifiedDate() {
-    let prefString = Preferences.get(TelemetryUtils.Preferences.AcceptedPolicyDate, "0");
+    let prefString = Services.prefs.getStringPref(TelemetryUtils.Preferences.AcceptedPolicyDate, "0");
     let valueInteger = parseInt(prefString, 10);
 
     // Bail out if we didn't store any value yet.
     if (valueInteger == 0) {
       this._log.info("get dataSubmissionPolicyNotifiedDate - No date stored yet.");
       return null;
     }
 
@@ -218,61 +217,61 @@ var TelemetryReportingPolicyImpl = {
   set dataSubmissionPolicyNotifiedDate(aDate) {
     this._log.trace("set dataSubmissionPolicyNotifiedDate - aDate: " + aDate);
 
     if (!aDate || aDate.getFullYear() < OLDEST_ALLOWED_ACCEPTANCE_YEAR) {
       this._log.error("set dataSubmissionPolicyNotifiedDate - Invalid notification date.");
       return;
     }
 
-    Preferences.set(TelemetryUtils.Preferences.AcceptedPolicyDate, aDate.getTime().toString());
+    Services.prefs.setStringPref(TelemetryUtils.Preferences.AcceptedPolicyDate, aDate.getTime().toString());
   },
 
   /**
    * Whether submission of data is allowed.
    *
    * This is the master switch for remote server communication. If it is
    * false, we never request upload or deletion.
    */
   get dataSubmissionEnabled() {
     // Default is true because we are opt-out.
-    return Preferences.get(TelemetryUtils.Preferences.DataSubmissionEnabled, true);
+    return Services.prefs.getBoolPref(TelemetryUtils.Preferences.DataSubmissionEnabled, true);
   },
 
   get currentPolicyVersion() {
-    return Preferences.get(TelemetryUtils.Preferences.CurrentPolicyVersion,
-                           TelemetryReportingPolicy.DEFAULT_DATAREPORTING_POLICY_VERSION);
+    return Services.prefs.getIntPref(TelemetryUtils.Preferences.CurrentPolicyVersion,
+                                     TelemetryReportingPolicy.DEFAULT_DATAREPORTING_POLICY_VERSION);
   },
 
   /**
    * The minimum policy version which for dataSubmissionPolicyAccepted to
    * to be valid.
    */
   get minimumPolicyVersion() {
-    const minPolicyVersion = Preferences.get(TelemetryUtils.Preferences.MinimumPolicyVersion, 1);
+    const minPolicyVersion = Services.prefs.getIntPref(TelemetryUtils.Preferences.MinimumPolicyVersion, 1);
 
     // First check if the current channel has a specific minimum policy version. If not,
     // use the general minimum policy version.
     let channel = "";
     try {
       channel = UpdateUtils.getUpdateChannel(false);
     } catch (e) {
       this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
       return minPolicyVersion;
     }
     const channelPref = TelemetryUtils.Preferences.MinimumPolicyVersion + ".channel-" + channel;
-    return Preferences.get(channelPref, minPolicyVersion);
+    return Services.prefs.getIntPref(channelPref, minPolicyVersion);
   },
 
   get dataSubmissionPolicyAcceptedVersion() {
-    return Preferences.get(TelemetryUtils.Preferences.AcceptedPolicyVersion, 0);
+    return Services.prefs.getIntPref(TelemetryUtils.Preferences.AcceptedPolicyVersion, 0);
   },
 
   set dataSubmissionPolicyAcceptedVersion(value) {
-    Preferences.set(TelemetryUtils.Preferences.AcceptedPolicyVersion, value);
+    Services.prefs.setIntPref(TelemetryUtils.Preferences.AcceptedPolicyVersion, value);
   },
 
   /**
    * Checks to see if the user has been notified about data submission
    * @return {Bool} True if user has been notified and the notification is still valid,
    *         false otherwise.
    */
   get isUserNotifiedOfCurrentPolicy() {
@@ -342,44 +341,44 @@ var TelemetryReportingPolicyImpl = {
     // If data submission is disabled, there's no point in showing the infobar. Just
     // forbid to upload.
     if (!this.dataSubmissionEnabled) {
       return false;
     }
 
     // Submission is enabled. We enable upload if user is notified or we need to bypass
     // the policy.
-    const bypassNotification = Preferences.get(TelemetryUtils.Preferences.BypassNotification, false);
+    const bypassNotification = Services.prefs.getBoolPref(TelemetryUtils.Preferences.BypassNotification, false);
     return this.isUserNotifiedOfCurrentPolicy || bypassNotification;
   },
 
   isFirstRun() {
     return this._isFirstRun;
   },
 
   /**
    * Migrate the data policy preferences, if needed.
    */
   _migratePreferences() {
     // Current prefs are mostly the same than the old ones, except for some deprecated ones.
     for (let pref of DEPRECATED_FHR_PREFS) {
-      Preferences.reset(pref);
+      Services.prefs.clearUserPref(pref);
     }
   },
 
   /**
    * Determine whether the user should be notified.
    */
   _shouldNotify() {
     if (!this.dataSubmissionEnabled) {
       this._log.trace("_shouldNotify - Data submission disabled by the policy.");
       return false;
     }
 
-    const bypassNotification = Preferences.get(TelemetryUtils.Preferences.BypassNotification, false);
+    const bypassNotification = Services.prefs.getBoolPref(TelemetryUtils.Preferences.BypassNotification, false);
     if (this.isUserNotifiedOfCurrentPolicy || bypassNotification) {
       this._log.trace("_shouldNotify - User already notified or bypassing the policy.");
       return false;
     }
 
     if (this._notificationInProgress) {
       this._log.trace("_shouldNotify - User not notified, notification already in progress.");
       return false;
@@ -426,17 +425,17 @@ var TelemetryReportingPolicyImpl = {
   /**
    * Try to open the privacy policy in a background tab instead of showing the infobar.
    */
   _openFirstRunPage() {
     if (!this._shouldNotify()) {
       return false;
     }
 
-    let firstRunPolicyURL = Preferences.get(TelemetryUtils.Preferences.FirsRunURL, "");
+    let firstRunPolicyURL = Services.prefs.getStringPref(TelemetryUtils.Preferences.FirsRunURL, "");
     if (!firstRunPolicyURL) {
       return false;
     }
     firstRunPolicyURL = Services.urlFormatter.formatURL(firstRunPolicyURL);
 
     let win;
     try {
       const { RecentWindow } = Cu.import("resource:///modules/RecentWindow.jsm", {});
@@ -485,20 +484,20 @@ var TelemetryReportingPolicyImpl = {
     return true;
   },
 
   observe(aSubject, aTopic, aData) {
     if (aTopic != "sessionstore-windows-restored") {
       return;
     }
 
-    this._isFirstRun = Preferences.get(TelemetryUtils.Preferences.FirstRun, true);
+    this._isFirstRun = Services.prefs.getBoolPref(TelemetryUtils.Preferences.FirstRun, true);
     if (this._isFirstRun) {
       // We're performing the first run, flip firstRun preference for subsequent runs.
-      Preferences.set(TelemetryUtils.Preferences.FirstRun, false);
+      Services.prefs.setBoolPref(TelemetryUtils.Preferences.FirstRun, false);
 
       try {
         if (this._openFirstRunPage()) {
           return;
         }
       } catch (e) {
         this._log.error("Failed to open privacy policy tab: " + e);
       }
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -16,17 +16,16 @@ this.EXPORTED_SYMBOLS = [
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/AppConstants.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/ClientID.jsm");
 Cu.import("resource://gre/modules/Log.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 Cu.import("resource://gre/modules/ServiceRequest.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
@@ -48,20 +47,21 @@ const Utils = TelemetryUtils;
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySend::";
 
 const TOPIC_IDLE_DAILY = "idle-daily";
 // The following topics are notified when Firefox is closing
 // because the OS is shutting down.
 const TOPIC_QUIT_APPLICATION_GRANTED = "quit-application-granted";
 const TOPIC_QUIT_APPLICATION_FORCED = "quit-application-forced";
+const PREF_CHANGED_TOPIC = "nsPref:changed";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
-const IS_UNIFIED_TELEMETRY = Preferences.get(TelemetryUtils.Preferences.Unified, false);
+const IS_UNIFIED_TELEMETRY = Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false);
 
 const PING_FORMAT_VERSION = 4;
 
 const MS_IN_A_MINUTE = 60 * 1000;
 
 const PING_TYPE_DELETION = "deletion";
 
 // We try to spread "midnight" pings out over this interval.
@@ -604,17 +604,17 @@ var TelemetrySendImpl = {
 
   OBSERVED_PREFERENCES: [
     TelemetryUtils.Preferences.TelemetryEnabled,
     TelemetryUtils.Preferences.FhrUploadEnabled,
   ],
 
   // Whether sending pings has been overridden.
   get _overrideOfficialCheck() {
-    return Preferences.get(TelemetryUtils.Preferences.OverrideOfficialCheck, false);
+    return Services.prefs.getBoolPref(TelemetryUtils.Preferences.OverrideOfficialCheck, false);
   },
 
   get _log() {
     if (!this._logger) {
       this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
     }
 
     return this._logger;
@@ -640,30 +640,32 @@ var TelemetrySendImpl = {
     this._annotateCrashReport();
 
     // Install the observer to detect OS shutdown early enough, so
     // that we catch this before the delayed setup happens.
     Services.obs.addObserver(this, TOPIC_QUIT_APPLICATION_FORCED);
     Services.obs.addObserver(this, TOPIC_QUIT_APPLICATION_GRANTED);
   },
 
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+
   async setup(testing) {
     this._log.trace("setup");
 
     this._testMode = testing;
     this._sendingEnabled = true;
 
     Services.obs.addObserver(this, TOPIC_IDLE_DAILY);
 
-    this._server = Preferences.get(TelemetryUtils.Preferences.Server, undefined);
+    this._server = Services.prefs.getStringPref(TelemetryUtils.Preferences.Server, undefined);
 
     // Annotate crash reports so that crash pings are sent correctly and listen
     // to pref changes to adjust the annotations accordingly.
     for (let pref of this.OBSERVED_PREFERENCES) {
-      Preferences.observe(pref, this._annotateCrashReport, this);
+      Services.prefs.addObserver(pref, this, true);
     }
     this._annotateCrashReport();
 
     // Check the pending pings on disk now.
     try {
       await this._checkPendingPings();
     } catch (ex) {
       this._log.error("setup - _checkPendingPings rejected", ex);
@@ -685,17 +687,17 @@ var TelemetrySendImpl = {
    */
   _annotateCrashReport() {
     try {
       const cr = Cc["@mozilla.org/toolkit/crash-reporter;1"];
       if (cr) {
         const crs = cr.getService(Ci.nsICrashReporter);
 
         let clientId = ClientID.getCachedClientID();
-        let server = this._server || Preferences.get(TelemetryUtils.Preferences.Server, undefined);
+        let server = this._server || Services.prefs.getStringPref(TelemetryUtils.Preferences.Server, undefined);
 
         if (!this.sendingEnabled() || !TelemetryReportingPolicy.canUpload()) {
           // If we cannot send pings then clear the crash annotations
           crs.annotateCrashReport("TelemetryClientId", "");
           crs.annotateCrashReport("TelemetryServerURL", "");
         } else {
           crs.annotateCrashReport("TelemetryClientId", clientId);
           crs.annotateCrashReport("TelemetryServerURL", server);
@@ -736,17 +738,17 @@ var TelemetrySendImpl = {
 
   async shutdown() {
     this._shutdown = true;
 
     for (let pref of this.OBSERVED_PREFERENCES) {
       // FIXME: When running tests this causes errors to be printed out if
       // TelemetrySend.shutdown() is called twice in a row without calling
       // TelemetrySend.setup() in-between.
-      Preferences.ignore(pref, this._annotateCrashReport, this);
+      Services.prefs.removeObserver(pref, this);
     }
 
     for (let topic of this.OBSERVER_TOPICS) {
       try {
         Services.obs.removeObserver(this, topic);
       } catch (ex) {
         this._log.error("shutdown - failed to remove observer for " + topic, ex);
       }
@@ -813,16 +815,21 @@ var TelemetrySendImpl = {
     case TOPIC_QUIT_APPLICATION_FORCED:
       setOSShutdown();
       break;
     case TOPIC_QUIT_APPLICATION_GRANTED:
       if (data == "syncShutdown") {
         setOSShutdown();
       }
       break;
+    case PREF_CHANGED_TOPIC:
+      if (this.OBSERVED_PREFERENCES.includes(data)) {
+        this._annotateCrashReport();
+      }
+      break;
     }
   },
 
   /**
    * Spawn the PingSender process that sends a ping. This function does
    * not return an error or throw, it only logs an error.
    *
    * Even if the function doesn't fail, it doesn't mean that the ping was
@@ -1233,17 +1240,17 @@ var TelemetrySendImpl = {
 
     // With unified Telemetry, the FHR upload setting controls whether we can send pings.
     // The Telemetry pref enables sending extended data sets instead.
     if (IS_UNIFIED_TELEMETRY) {
       // Deletion pings are sent even if the upload is disabled.
       if (ping && isDeletionPing(ping)) {
         return true;
       }
-      return Preferences.get(TelemetryUtils.Preferences.FhrUploadEnabled, false);
+      return Services.prefs.getBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, false);
     }
 
     // Without unified Telemetry, the Telemetry enabled pref controls ping sending.
     return Utils.isTelemetryEnabled;
   },
 
   /**
    * Track any pending ping send and save tasks through the promise passed here.
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -9,17 +9,16 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/DeferredTask.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
 
 const Utils = TelemetryUtils;
@@ -38,59 +37,59 @@ const REASON_GATHER_PAYLOAD = "gather-pa
 const REASON_GATHER_SUBSESSION_PAYLOAD = "gather-subsession-payload";
 const REASON_TEST_PING = "test-ping";
 const REASON_ENVIRONMENT_CHANGE = "environment-change";
 const REASON_SHUTDOWN = "shutdown";
 
 const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
-const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 5 * 60) * 1000;
+const MIN_SUBSESSION_LENGTH_MS = Services.prefs.getIntPref("toolkit.telemetry.minSubsessionLength", 5 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
 const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
 const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
 const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
 const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
 const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
 
 const DATAREPORTING_DIRECTORY = "datareporting";
 const ABORTED_SESSION_FILE_NAME = "aborted-session-ping";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
-const IS_UNIFIED_TELEMETRY = Preferences.get(TelemetryUtils.Preferences.Unified, false);
+const IS_UNIFIED_TELEMETRY = Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false);
 
 // Maximum number of content payloads that we are willing to store.
 const MAX_NUM_CONTENT_PAYLOADS = 10;
 
 // Do not gather data more than once a minute (ms)
 const TELEMETRY_INTERVAL = 60 * 1000;
 // Delay before intializing telemetry (ms)
-const TELEMETRY_DELAY = Preferences.get("toolkit.telemetry.initDelay", 60) * 1000;
+const TELEMETRY_DELAY = Services.prefs.getIntPref("toolkit.telemetry.initDelay", 60) * 1000;
 // Delay before initializing telemetry if we're testing (ms)
 const TELEMETRY_TEST_DELAY = 1;
 // Execute a scheduler tick every 5 minutes.
-const SCHEDULER_TICK_INTERVAL_MS = Preferences.get("toolkit.telemetry.scheduler.tickInterval", 5 * 60) * 1000;
+const SCHEDULER_TICK_INTERVAL_MS = Services.prefs.getIntPref("toolkit.telemetry.scheduler.tickInterval", 5 * 60) * 1000;
 // When user is idle, execute a scheduler tick every 60 minutes.
-const SCHEDULER_TICK_IDLE_INTERVAL_MS = Preferences.get("toolkit.telemetry.scheduler.idleTickInterval", 60 * 60) * 1000;
+const SCHEDULER_TICK_IDLE_INTERVAL_MS = Services.prefs.getIntPref("toolkit.telemetry.scheduler.idleTickInterval", 60 * 60) * 1000;
 
 // The tolerance we have when checking if it's midnight (15 minutes).
 const SCHEDULER_MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
 
 // The maximum time (ms) until the tick should moved from the idle
 // queue to the regular queue if it hasn't been executed yet.
 const SCHEDULER_TICK_MAX_IDLE_DELAY_MS = 60 * 1000;
 
 // Seconds of idle time before pinging.
 // On idle-daily a gather-telemetry notification is fired, during it probes can
 // start asynchronous tasks to gather data.
-const IDLE_TIMEOUT_SECONDS = Preferences.get("toolkit.telemetry.idleTimeout", 5 * 60);
+const IDLE_TIMEOUT_SECONDS = Services.prefs.getIntPref("toolkit.telemetry.idleTimeout", 5 * 60);
 
 // The frequency at which we persist session data to the disk to prevent data loss
 // in case of aborted sessions (currently 5 minutes).
 const ABORTED_SESSION_UPDATE_INTERVAL_MS = 5 * 60 * 1000;
 
 const TOPIC_CYCLE_COLLECTOR_BEGIN = "cycle-collector-begin";
 
 // How long to wait in millis for all the child memory reports to come in
@@ -1502,22 +1501,22 @@ var Impl = {
     annotateCrashReport(this._sessionId);
 
     // Initialize some probes that are kept in their own modules
     this._thirdPartyCookies = new ThirdPartyCookieProbe();
     this._thirdPartyCookies.init();
 
     // Record old value and update build ID preference if this is the first
     // run with a new build ID.
-    let previousBuildId = Preferences.get(TelemetryUtils.Preferences.PreviousBuildID, null);
+    let previousBuildId = Services.prefs.getStringPref(TelemetryUtils.Preferences.PreviousBuildID, null);
     let thisBuildID = Services.appinfo.appBuildID;
     // If there is no previousBuildId preference, we send null to the server.
     if (previousBuildId != thisBuildID) {
       this._previousBuildId = previousBuildId;
-      Preferences.set(TelemetryUtils.Preferences.PreviousBuildID, thisBuildID);
+      Services.prefs.setStringPref(TelemetryUtils.Preferences.PreviousBuildID, thisBuildID);
     }
 
     this.attachEarlyObservers();
 
     ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
   },
@@ -1796,19 +1795,19 @@ var Impl = {
     let p = [];
 
     if (IS_UNIFIED_TELEMETRY) {
       let shutdownPayload = this.getSessionPayload(REASON_SHUTDOWN, false);
 
       // Only send the shutdown ping using the pingsender from the second
       // browsing session on, to mitigate issues with "bot" profiles (see bug 1354482).
       const sendOnThisSession =
-        Preferences.get(Utils.Preferences.ShutdownPingSenderFirstSession, false) ||
+        Services.prefs.getBoolPref(Utils.Preferences.ShutdownPingSenderFirstSession, false) ||
         !TelemetryReportingPolicy.isFirstRun();
-      let sendWithPingsender = Preferences.get(TelemetryUtils.Preferences.ShutdownPingSender, false) &&
+      let sendWithPingsender = Services.prefs.getBoolPref(TelemetryUtils.Preferences.ShutdownPingSender, false) &&
                                sendOnThisSession;
 
       let options = {
         addClientId: true,
         addEnvironment: true,
         usePingSender: sendWithPingsender,
       };
       p.push(TelemetryController.submitExternalPing(getPingType(shutdownPayload), shutdownPayload, options)
--- a/toolkit/components/telemetry/TelemetryUtils.jsm
+++ b/toolkit/components/telemetry/TelemetryUtils.jsm
@@ -5,17 +5,16 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "TelemetryUtils"
 ];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
-Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
 const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
 
 const IS_CONTENT_PROCESS = (function() {
   // We cannot use Services.appinfo here because in telemetry xpcshell tests,
@@ -64,17 +63,17 @@ this.TelemetryUtils = {
     return IS_CONTENT_PROCESS;
   },
 
   /**
    * Returns the state of the Telemetry enabled preference, making sure
    * it correctly evaluates to a boolean type.
    */
   get isTelemetryEnabled() {
-    return Preferences.get(PREF_TELEMETRY_ENABLED, false) === true;
+    return Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED, false) === true;
   },
 
   /**
    * Turn a millisecond timestamp into a day timestamp.
    *
    * @param aMsec A number of milliseconds since Unix epoch.
    * @return The number of whole days since Unix epoch.
    */
--- a/toolkit/components/telemetry/UpdatePing.jsm
+++ b/toolkit/components/telemetry/UpdatePing.jsm
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
                                   "resource://gre/modules/TelemetryController.jsm");
 
 const LOGGER_NAME = "Toolkit.Telemetry";
@@ -25,17 +24,17 @@ this.EXPORTED_SYMBOLS = ["UpdatePing"];
 /**
  * This module is responsible for listening to all the relevant update
  * signals, gathering the needed information and assembling the "update"
  * ping.
  */
 this.UpdatePing = {
   earlyInit() {
     this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, "UpdatePing::");
-    this._enabled = Preferences.get(TelemetryUtils.Preferences.UpdatePing, false);
+    this._enabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.UpdatePing, false);
 
     this._log.trace("init - enabled: " + this._enabled);
 
     if (!this._enabled) {
       return;
     }
 
     Services.obs.addObserver(this, UPDATE_DOWNLOADED_TOPIC);
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
@@ -3,22 +3,22 @@
 */
 
 // This tests the public Telemetry API for submitting pings.
 
 "use strict";
 
 Cu.import("resource://gre/modules/TelemetryController.jsm", this);
 Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
+Cu.import("resource://testing-common/MockRegistrar.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
 Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
-Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryHealthPing",
   "resource://gre/modules/TelemetryHealthPing.jsm");
 
 const MS_IN_A_MINUTE = 60 * 1000;
 
@@ -78,17 +78,17 @@ function histogramValueCount(h) {
 }
 
 add_task(async function test_setup() {
   // Trigger a proper telemetry init.
   do_get_profile(true);
   // Make sure we don't generate unexpected pings due to pref changes.
   await setEmptyPrefWatchlist();
   Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, true);
-  Preferences.set(TelemetryUtils.Preferences.HealthPingEnabled, true);
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.HealthPingEnabled, true);
 });
 
 // Test the ping sending logic.
 add_task(async function test_sendPendingPings() {
   const TYPE_PREFIX = "test-sendPendingPings-";
   const TEST_TYPE_A = TYPE_PREFIX + "A";
   const TEST_TYPE_B = TYPE_PREFIX + "B";
 
@@ -136,17 +136,17 @@ add_task(async function test_sendPending
   Assert.equal(histSendTimeSuccess.snapshot().sum, 0,
                "Should not have recorded any sending in histograms yet.");
   Assert.equal(histSendTimeFail.snapshot().sum, 0,
                "Should not have recorded any sending in histograms yet.");
 
   // Now enable sending to the ping server.
   now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
   PingServer.start();
-  Preferences.set(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port);
+  Services.prefs.setStringPref(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port);
 
   let timerPromise = waitForTimer();
   await TelemetryController.testReset();
   let [pingSendTimerCallback, pingSendTimeout] = await timerPromise;
   Assert.ok(!!pingSendTimerCallback, "Should have a timer callback");
 
   // We should have received 10 pings from the first send batch:
   // 5 of type B and 5 of type A, as sending is newest-first.
@@ -438,45 +438,45 @@ add_task(async function test_sendCheckOv
   const TEST_PING_TYPE = "test-sendCheckOverride";
 
   // Clear any pending pings.
   await TelemetryController.testShutdown();
   await TelemetryStorage.testClearPendingPings();
 
   // Enable the ping server.
   PingServer.start();
-  Preferences.set(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port);
+  Services.prefs.setStringPref(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port);
 
   // Start Telemetry and disable the test-mode so pings don't get
   // sent unless we enable the override.
   await TelemetryController.testReset();
 
   // Submit a test ping and make sure it doesn't get sent. We only do
   // that if we're on unofficial builds: pings will always get sent otherwise.
   if (!Services.telemetry.isOfficialTelemetry) {
     TelemetrySend.setTestModeEnabled(false);
     PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
 
     await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" });
     Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings");
   }
 
   // Enable the override and try to send again.
-  Preferences.set(TelemetryUtils.Preferences.OverrideOfficialCheck, true);
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.OverrideOfficialCheck, true);
   PingServer.resetPingHandler();
   await TelemetrySend.reset();
   await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" });
 
   // Make sure we received the ping.
   const ping = await PingServer.promiseNextPing();
   Assert.equal(ping.type, TEST_PING_TYPE, "Must receive a ping of the expected type");
 
   // Restore the test mode and disable the override.
   TelemetrySend.setTestModeEnabled(true);
-  Preferences.reset(TelemetryUtils.Preferences.OverrideOfficialCheck);
+  Services.prefs.clearUserPref(TelemetryUtils.Preferences.OverrideOfficialCheck);
 });
 
 add_task(async function test_measurePingsSize() {
   const TEST_TYPE = "test-measure-ping-size";
 
   let histSuccessPingSize = Telemetry.getHistogramById("TELEMETRY_SUCCESSFUL_SEND_PINGS_SIZE_KB");
   let histFailedPingSize = Telemetry.getHistogramById("TELEMETRY_FAILED_SEND_PINGS_SIZE_KB");
 
@@ -517,12 +517,72 @@ add_task(async function test_measurePing
 
   // Check that we recorded the ping sizes correctly into histograms.
   Assert.equal(histogramValueCount(histSuccessPingSize.snapshot()), 2,
     "Should have recorded 2 successful ping into histogram.");
   Assert.equal(histogramValueCount(histFailedPingSize.snapshot()), 1,
     "Should have recorded 1 failed ping into histogram.");
 });
 
+add_task(async function test_pref_observer() {
+  await TelemetrySend.setup(true);
+
+  let origTelemetryEnabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.TelemetryEnabled);
+  let origFhrUploadEnabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled);
+
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, true);
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true);
+
+  function waitAnnotateCrashReport(expectedValue, trigger) {
+    return new Promise(function(resolve, reject) {
+      let keys = new Set(["TelemetryClientId", "TelemetryServerURL"]);
+
+      let crs = {
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsICrashReporter]),
+        annotateCrashReport(key, value) {
+          if (!keys.delete(key)) {
+            MockRegistrar.unregister(gMockCrs);
+            reject(Error(`Crash report annotation with unexpected key: "${key}".`));
+          }
+
+          if (expectedValue && value == "") {
+            MockRegistrar.unregister(gMockCrs);
+            reject(Error("Crash report annotation without expected value."));
+          }
+
+          if (!expectedValue && value != "") {
+            MockRegistrar.unregister(gMockCrs);
+            reject(Error(`Crash report annotation ("${key}") with unexpected value: "${value}".`));
+          }
+
+          if (keys.size == 0) {
+            MockRegistrar.unregister(gMockCrs);
+            resolve();
+          }
+        },
+        UpdateCrashEventsDir() {
+        },
+      };
+
+      let gMockCrs = MockRegistrar.register("@mozilla.org/toolkit/crash-reporter;1", crs);
+      do_register_cleanup(function() {
+        MockRegistrar.unregister(gMockCrs);
+      });
+
+      trigger();
+    });
+  }
+
+  await waitAnnotateCrashReport(true, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, false));
+
+  await waitAnnotateCrashReport(true, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, true));
+
+  await waitAnnotateCrashReport(false, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, false));
+
+  await waitAnnotateCrashReport(true, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true));
+
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, origTelemetryEnabled);
+  Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, origFhrUploadEnabled);
+});
 
 add_task(async function cleanup() {
   await PingServer.stop();
 });