Bug 1192924: Expose the update URL formatting code a new UpdateUtils module. r=rstrong
☠☠ backed out by 3e33e2914ac5 ☠ ☠
authorDave Townsend <dtownsend@oxymoronical.com>
Mon, 14 Sep 2015 11:04:19 -0700
changeset 263844 10e1fa2cc23a348e0266d28a76bf932fd2efdf2f
parent 263843 812b25892cb6951f20aa754e47f089144f95e79b
child 263845 a6860f880e01572924f05421adf26bee06a72cb4
push id65448
push userkwierso@gmail.com
push dateTue, 22 Sep 2015 23:46:56 +0000
treeherdermozilla-inbound@eb9c01341845 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs1192924
milestone44.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 1192924: Expose the update URL formatting code a new UpdateUtils module. r=rstrong The GMP manager uses a copy of the update service's url formatting code and has since fallen out of sync. We'll also want to use the same formatting code for the system add-on update checks so this just exposes it in a shared API. I've moved the contents of UpdateChannel.jsm to UpdateUtils.jsm and exposed formatUpdateURL there as well as a few properties that the update service still needs access to. UpdateUtils.UpdateChannel is intended to be a lazy getter but isn't for now since tests expect to be able to change the update channel at runtime.
browser/base/content/aboutDialog.js
browser/base/content/browser.js
browser/base/content/newtab/newTab.js
browser/base/content/test/general/browser_remoteTroubleshoot.js
browser/components/loop/modules/MozLoopAPI.jsm
browser/components/nsBrowserGlue.js
browser/components/preferences/in-content/main.js
browser/experiments/Experiments.jsm
browser/modules/DirectoryLinksProvider.jsm
mobile/android/chrome/content/aboutFeedback.js
netwerk/protocol/http/UserAgentUpdates.jsm
services/datareporting/policy.jsm
services/datareporting/tests/xpcshell/test_policy.js
services/healthreport/healthreporter.jsm
services/healthreport/providers.jsm
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetryEnvironment.jsm
toolkit/components/telemetry/TelemetryReportingPolicy.jsm
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/content/tests/unit/test_updateChannelModule.js
toolkit/content/tests/unit/xpcshell.ini
toolkit/modules/GMPInstallManager.jsm
toolkit/modules/GMPUtils.jsm
toolkit/modules/Troubleshoot.jsm
toolkit/modules/UpdateChannel.jsm
toolkit/modules/UpdateUtils.jsm
toolkit/modules/moz.build
toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js
toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
toolkit/modules/tests/xpcshell/xpcshell.ini
toolkit/mozapps/extensions/internal/GMPProvider.jsm
toolkit/mozapps/extensions/nsBlocklistService.js
toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
toolkit/mozapps/update/nsUpdateService.js
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -67,35 +67,35 @@ function init(aEvent)
   }
 
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
   let defaults = Services.prefs.getDefaultBranch("");
   let channelLabel = document.getElementById("currentChannel");
   let currentChannelText = document.getElementById("currentChannelText");
-  channelLabel.value = UpdateChannel.get();
+  channelLabel.value = UpdateUtils.UpdateChannel;
   if (/^release($|\-)/.test(channelLabel.value))
       currentChannelText.hidden = true;
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
 
 #ifdef MOZ_UPDATER
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 var gAppUpdater;
 
 function onUnload(aEvent) {
   if (gAppUpdater.isChecking)
     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
   // Safe to call even when there isn't a download in progress.
   gAppUpdater.removeDownloadListener();
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -37,18 +37,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
                                   "resource:///modules/AboutHome.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
                                    "@mozilla.org/browser/favicon-service;1",
                                    "mozIAsyncFavicons");
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
@@ -2913,17 +2913,17 @@ var BrowserOnClick = {
       port: location.port,
       timestamp: Math.round(Date.now() / 1000),
       errorCode: transportSecurityInfo.errorCode,
       failedCertChain: asciiCertChain,
       userAgent: window.navigator.userAgent,
       version: 1,
       build: gAppInfo.appBuildID,
       product: gAppInfo.name,
-      channel: UpdateChannel.get()
+      channel: UpdateUtils.UpdateChannel
     }
 
     let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url");
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
         .createInstance(Ci.nsIXMLHttpRequest);
     try {
       xhr.open("POST", reportURL);
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -13,18 +13,16 @@ Cu.import("resource://gre/modules/PageTh
 Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm");
 Cu.import("resource:///modules/DirectoryLinksProvider.jsm");
 Cu.import("resource://gre/modules/NewTabUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Rect",
   "resource://gre/modules/Geometry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-  "resource://gre/modules/UpdateChannel.jsm");
 
 var {
   links: gLinks,
   allPages: gAllPages,
   linkChecker: gLinkChecker,
   pinnedLinks: gPinnedLinks,
   blockedLinks: gBlockedLinks,
   gridPrefs: gGridPrefs
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js
+++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js
@@ -55,17 +55,17 @@ add_task(function*() {
   Assert.ok(got.message.graphics, "should have graphics");
 
   // Check we have channel and build ID info:
   Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID,
                "should have correct build ID");
 
   let updateChannel = null;
   try {
-    updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
+    updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
   } catch (ex) {}
   if (!updateChannel) {
     Assert.ok(!('updateChannel' in got.message.application),
                 "should not have update channel where not available.");
   } else {
     Assert.equal(got.message.application.updateChannel, updateChannel,
                  "should have correct update channel.");
   }
--- a/browser/components/loop/modules/MozLoopAPI.jsm
+++ b/browser/components/loop/modules/MozLoopAPI.jsm
@@ -20,18 +20,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
                                         "resource:///modules/loop/LoopStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
                                         "resource://gre/modules/MozSocialAPI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
                                         "resource://gre/modules/PageMetadata.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                         "resource://gre/modules/PluralForm.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                        "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                        "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                         "resource:///modules/UITour.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Social",
                                         "resource:///modules/Social.jsm");
 XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
   return Cc["@mozilla.org/xre/app-info;1"]
            .getService(Ci.nsIXULAppInfo)
            .QueryInterface(Ci.nsIXULRuntime);
@@ -805,17 +805,17 @@ function injectLoopAPI(targetWindow) {
     appVersionInfo: {
       enumerable: true,
       get: function() {
         if (!appVersionInfo) {
           // If the lazy getter explodes, we're probably loaded in xpcshell,
           // which doesn't have what we need, so log an error.
           try {
             appVersionInfo = Cu.cloneInto({
-              channel: UpdateChannel.get(),
+              channel: UpdateUtils.UpdateChannel,
               version: appInfo.version,
               OS: appInfo.OS
             }, targetWindow);
           } catch (ex) {
             // only log outside of xpcshell to avoid extra message noise
             if (typeof targetWindow !== 'undefined' && "console" in targetWindow) {
               MozLoopService.log.error("Failed to construct appVersionInfo; if this isn't " +
                                        "an xpcshell unit test, something is wrong", ex);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -119,18 +119,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
                                   "resource:///modules/SignInToWebsite.jsm");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
 #ifdef E10S_TESTING_ONLY
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
                                   "resource:///modules/ContentCrashReporters.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                   "resource:///modules/ContentCrashReporters.jsm");
 #endif
@@ -2972,17 +2972,17 @@ var E10SUINotification = {
   // e10s testing period to Nightly users.
   CURRENT_NOTICE_COUNT: 4,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
-      let updateChannel = UpdateChannel.get();
+      let updateChannel = UpdateUtils.UpdateChannel;
       let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
 
       skipE10sChecks = !channelAuthorized ||
                        Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y");
     } catch(e) {}
 
     if (skipE10sChecks) {
       return;
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -160,18 +160,18 @@ var gMainPane = {
       shouldProceed = !cancelQuit.data;
 
       if (shouldProceed) {
         for (let prefToChange of prefsToChange) {
           prefToChange.value = e10sCheckbox.checked;
         }
 
         let tmp = {};
-        Components.utils.import("resource://gre/modules/UpdateChannel.jsm", tmp);
-        if (!e10sCheckbox.checked && tmp.UpdateChannel.get() != "default") {
+        Components.utils.import("resource://gre/modules/UpdateUtils.jsm", tmp);
+        if (!e10sCheckbox.checked && tmp.UpdateUtils.UpdateChannel != "default") {
           Services.prefs.setBoolPref("browser.requestE10sFeedback", true);
           Services.prompt.alert(window, brandName, bundle.getString("e10sFeedbackAfterRestart"));
         }
         Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
       }
     }
 
     // Revert the checkbox in case we didn't quit
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -15,18 +15,18 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
@@ -270,17 +270,17 @@ Experiments.Policy.prototype = {
     return new Date(this.now().getTime() + offset);
   },
 
   oneshotTimer: function (callback, timeout, thisObj, name) {
     return CommonUtils.namedTimer(callback, timeout, thisObj, name);
   },
 
   updatechannel: function () {
-    return UpdateChannel.get();
+    return UpdateUtils.UpdateChannel;
   },
 
   locale: function () {
     let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
     return chrome.getSelectedLocale("global");
   },
 
   /**
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -21,18 +21,18 @@ Cu.import("resource://gre/modules/Timer.
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
   "resource://gre/modules/NewTabUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
   "resource://gre/modules/osfile.jsm")
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
   "@mozilla.org/network/effective-tld-service;1",
   "nsIEffectiveTLDService");
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
   return new TextDecoder();
 });
 XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
   return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
@@ -275,17 +275,17 @@ var DirectoryLinksProvider = {
       this._setupStartEndTime(link);
       this._suggestedLinks.set(suggestedSite, suggestedMap);
     }
   },
 
   _fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
     // Replace with the same display locale used for selecting links data
     uri = uri.replace("%LOCALE%", this.locale);
-    uri = uri.replace("%CHANNEL%", UpdateChannel.get());
+    uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
 
     return this._downloadJsonData(uri).then(json => {
       return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
     });
   },
 
   /**
    * Downloads a links with json content
--- a/mobile/android/chrome/content/aboutFeedback.js
+++ b/mobile/android/chrome/content/aboutFeedback.js
@@ -17,17 +17,17 @@ const HEARTY_ICON_XXHDPI = "chrome://bro
 
 const FLOATY_ICON_MDPI = "chrome://browser/skin/images/icon_floaty_mdpi.png";
 const FLOATY_ICON_HDPI = "chrome://browser/skin/images/icon_floaty_hdpi.png";
 const FLOATY_ICON_XHDPI = "chrome://browser/skin/images/icon_floaty_xhdpi.png";
 const FLOATY_ICON_XXHDPI = "chrome://browser/skin/images/icon_floaty_xxhdpi.png";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Messaging.jsm");
-Cu.import("resource://gre/modules/UpdateChannel.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 document.addEventListener("DOMContentLoaded", init, false);
 
 function dump(a) {
   Services.console.logStringMessage(a);
 }
 
 function init() {
   let anchors = document.querySelectorAll(".maybe-later");
@@ -133,17 +133,17 @@ function sendFeedback(aEvent) {
     data["url"] = urlElement.value;
   }
 
   data["device"] = Services.sysinfo.get("device");
   data["manufacturer"] = Services.sysinfo.get("manufacturer");
   data["platform"] = Services.appinfo.OS;
   data["version"] = Services.appinfo.version;
   data["locale"] = Services.locale.getSystemLocale().getCategory("NSILOCALE_CTYPE");
-  data["channel"] = UpdateChannel.get();
+  data["channel"] = UpdateUtils.UpdateChannel;
 
   // Source field is added only when Fennec prompts the user.
   let getParam = window.location.href.split("?");
   if (getParam.length > 1) {
     let urlParam = new URLSearchParams(getParam[1]);
     if(urlParam.get("source")) {
       data["source"] = urlParam.get("source");
     }
--- a/netwerk/protocol/http/UserAgentUpdates.jsm
+++ b/netwerk/protocol/http/UserAgentUpdates.jsm
@@ -22,17 +22,17 @@ XPCOMUtils.defineLazyModuleGetter(
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "OS", "resource://gre/modules/osfile.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "Promise", "resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
-  this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
+  this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
 
 XPCOMUtils.defineLazyGetter(this, "gApp",
   function() {
     return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo)
                                             .QueryInterface(Ci.nsIXULRuntime);
@@ -187,17 +187,17 @@ this.UserAgentUpdates = {
   _getParameters() {
     return {
       "%DATE%": function() { return Date.now().toString(); },
       "%PRODUCT%": function() { return gApp.name; },
       "%APP_ID%": function() { return gApp.ID; },
       "%APP_VERSION%": function() { return gApp.version; },
       "%BUILD_ID%": function() { return gApp.appBuildID; },
       "%OS%": function() { return gApp.OS; },
-      "%CHANNEL%": function() { return UpdateChannel.get(); },
+      "%CHANNEL%": function() { return UpdateUtils.UpdateChannel; },
       "%DISTRIBUTION%": function() { return this._getPref(PREF_APP_DISTRIBUTION, ""); },
       "%DISTRIBUTION_VERSION%": function() { return this._getPref(PREF_APP_DISTRIBUTION_VERSION, ""); },
     };
   },
 
   _getUpdateURL: function() {
     let url = this._getPref(PREF_UPDATES_URL, "");
     let params = this._getParameters();
--- a/services/datareporting/policy.jsm
+++ b/services/datareporting/policy.jsm
@@ -20,17 +20,17 @@ this.EXPORTED_SYMBOLS = [
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 #endif
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource://gre/modules/UpdateChannel.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 // The current policy version number. If the version number stored in the prefs
 // is smaller than this, data upload will be disabled until the user is re-notified
 // about the policy changes.
 const DATAREPORTING_POLICY_VERSION = 1;
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
@@ -383,17 +383,17 @@ this.DataReportingPolicy.prototype = Obj
   },
 
   /**
    * The minimum policy version which for dataSubmissionPolicyAccepted to
    * to be valid.
    */
   get minimumPolicyVersion() {
     // First check if the current channel has an ove
-    let channel = UpdateChannel.get(false);
+    let channel = UpdateUtils.getUpdateChannel(false);
     let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel);
     return channelPref !== undefined ?
            channelPref : this._prefs.get("minimumPolicyVersion", 1);
   },
 
   get dataSubmissionPolicyAcceptedVersion() {
     return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0);
   },
--- a/services/datareporting/tests/xpcshell/test_policy.js
+++ b/services/datareporting/tests/xpcshell/test_policy.js
@@ -3,32 +3,32 @@
 
 "use strict";
 
 const {utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
 Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
-Cu.import("resource://gre/modules/UpdateChannel.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 function getPolicy(name,
                    aCurrentPolicyVersion = 1,
                    aMinimumPolicyVersion = 1,
                    aBranchMinimumVersionOverride) {
   let branch = "testing.datareporting." + name;
 
   // The version prefs should not be removed on reset, so set them in the
   // default branch.
   let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy."
                                            , defaultBranch: true });
   defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion);
   defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion);
-  let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false);
+  let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateUtils.getUpdateChannel(false);
   if (aBranchMinimumVersionOverride !== undefined)
     defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride);
   else
     defaultPolicyPrefs.reset(branchOverridePrefName);
 
   let policyPrefs = new Preferences(branch + ".policy.");
   let healthReportPrefs = new Preferences(branch + ".healthreport.");
 
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -25,18 +25,18 @@ Cu.import("resource://gre/modules/osfile
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
                                   "resource://gre/modules/TelemetryController.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 // Oldest year to allow in date preferences. This module was implemented in
 // 2012 and no dates older than that should be encountered.
 const OLDEST_ALLOWED_YEAR = 2012;
 
 const DAYS_IN_PAYLOAD = 180;
 
 const DEFAULT_DATABASE_NAME = "healthreport.sqlite";
@@ -1098,17 +1098,17 @@ AbstractHealthReporter.prototype = Objec
         out[k] = ai[v];
       }
     } catch (ex) {
       this._log.warn("Could not obtain Services.appinfo: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     try {
-      out["updateChannel"] = UpdateChannel.get();
+      out["updateChannel"] = UpdateUtils.UpdateChannel;
     } catch (ex) {
       this._log.warn("Could not obtain update channel: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     return out;
   },
 });
--- a/services/healthreport/providers.jsm
+++ b/services/healthreport/providers.jsm
@@ -41,18 +41,18 @@ Cu.import("resource://gre/modules/osfile
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-common/utils.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
                                   "resource://gre/modules/PlacesDBUtils.jsm");
 
 
 const LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_LAST_NUMERIC};
 const LAST_TEXT_FIELD = {type: Metrics.Storage.FIELD_LAST_TEXT};
 const DAILY_DISCRETE_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_DISCRETE_NUMERIC};
 const DAILY_LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC};
@@ -328,17 +328,17 @@ AppInfoProvider.prototype = Object.freez
       try {
         yield m.setLastText(k, ai[v]);
       } catch (ex) {
         this._log.warn("Error obtaining Services.appinfo." + v);
       }
     }
 
     try {
-      yield m.setLastText("updateChannel", UpdateChannel.get());
+      yield m.setLastText("updateChannel", UpdateUtils.UpdateChannel);
     } catch (ex) {
       this._log.warn("Could not obtain update channel: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     yield m.setLastText("distributionID", this._prefs.get("distribution.id", ""));
     yield m.setLastText("distributionVersion", this._prefs.get("distribution.version", ""));
     yield m.setLastText("hotfixVersion", this._prefs.get("extensions.hotfix.lastVersion", ""));
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -75,18 +75,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
                                   "resource://gre/modules/SessionRecorder.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
                                   "resource://gre/modules/TelemetryArchive.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
                                   "resource://gre/modules/TelemetrySession.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy",
                                   "resource://gre/modules/TelemetryReportingPolicy.jsm");
@@ -407,17 +407,17 @@ var Impl = {
     try {
       arch = Services.sysinfo.get("arch");
     } catch (e) {
       this._log.trace("assemblePing - Unable to get system architecture.", e);
     }
 
     let updateChannel = null;
     try {
-      updateChannel = UpdateChannel.get(false);
+      updateChannel = UpdateUtils.getUpdateChannel(false);
     } catch (e) {
       this._log.trace("assemblePing - Unable to get update channel.", e);
     }
 
     return {
       architecture: arch,
       buildId: Services.appinfo.appBuildID,
       name: Services.appinfo.name,
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -27,18 +27,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/ctypes.jsm");
 #ifndef MOZ_WIDGET_GONK
 Cu.import("resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 #endif
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
                                   "resource://gre/modules/ProfileAge.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 const CHANGE_THROTTLE_INTERVAL_MS = 5 * 60 * 1000;
 
 /**
  * This is a policy object used to override behavior for testing.
  */
 var Policy = {
   now: () => new Date(),
@@ -1002,17 +1002,17 @@ EnvironmentCache.prototype = {
   },
 
   /**
    * Update the cached settings data.
    */
   _updateSettings: function () {
     let updateChannel = null;
     try {
-      updateChannel = UpdateChannel.get(false);
+      updateChannel = UpdateUtils.getUpdateChannel(false);
     } catch (e) {}
 
     this._currentEnvironment.settings = {
 #ifndef MOZ_WIDGET_GONK
       addonCompatibilityCheckEnabled: AddonManager.checkCompatibility,
 #endif
       blocklistEnabled: Preferences.get(PREF_BLOCKLIST_ENABLED, true),
 #ifndef MOZ_WIDGET_ANDROID
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -14,18 +14,18 @@ Cu.import("resource://gre/modules/Log.js
 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);
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetryReportingPolicy::";
 
 // Oldest year to allow in date preferences. The FHR infobar was implemented in
 // 2012 and no dates older than that should be encountered.
 const OLDEST_ALLOWED_ACCEPTANCE_YEAR = 2012;
 
@@ -246,17 +246,17 @@ var TelemetryReportingPolicyImpl = {
    */
   get minimumPolicyVersion() {
     const minPolicyVersion = Preferences.get(PREF_MINIMUM_POLICY_VERSION, 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 = UpdateChannel.get(false);
+      channel = UpdateUtils.getUpdateChannel(false);
     } catch(e) {
       this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
       return minPolicyVersion;
     }
     const channelPref = PREF_MINIMUM_POLICY_VERSION + ".channel-" + channel;
     return Preferences.get(channelPref, minPolicyVersion);
   },
 
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -138,18 +138,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
                                   "resource://gre/modules/UITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 
 function generateUUID() {
   let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   // strip {}
--- a/toolkit/components/urlformatter/nsURLFormatter.js
+++ b/toolkit/components/urlformatter/nsURLFormatter.js
@@ -20,18 +20,18 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const PREF_APP_DISTRIBUTION           = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 function nsURLFormatterService() {
   XPCOMUtils.defineLazyGetter(this, "appInfo", function UFS_appInfo() {
     return Cc["@mozilla.org/xre/app-info;1"].
            getService(Ci.nsIXULAppInfo).
            QueryInterface(Ci.nsIXULRuntime);
   });
 
@@ -106,17 +106,17 @@ nsURLFormatterService.prototype = {
     APPBUILDID:       function() this.appInfo.appBuildID,
     PLATFORMVERSION:  function() this.appInfo.platformVersion,
     PLATFORMBUILDID:  function() this.appInfo.platformBuildID,
     APP:              function() this.appInfo.name.toLowerCase().replace(/ /, ""),
     OS:               function() this.appInfo.OS,
     XPCOMABI:         function() this.ABI,
     BUILD_TARGET:     function() this.appInfo.OS + "_" + this.ABI,
     OS_VERSION:       function() this.OSVersion,
-    CHANNEL:          function() UpdateChannel.get(),
+    CHANNEL:          function() UpdateUtils.UpdateChannel,
     MOZILLA_API_KEY:   function() "@MOZ_MOZILLA_API_KEY@",
     GOOGLE_API_KEY:   function() "@MOZ_GOOGLE_API_KEY@",
     GOOGLE_OAUTH_API_CLIENTID:function() "@MOZ_GOOGLE_OAUTH_API_CLIENTID@",
     GOOGLE_OAUTH_API_KEY:     function() "@MOZ_GOOGLE_OAUTH_API_KEY@",
     BING_API_CLIENTID:function() "@MOZ_BING_API_CLIENTID@",
     BING_API_KEY:     function() "@MOZ_BING_API_KEY@",
     DISTRIBUTION:     function() this.distribution.id,
     DISTRIBUTION_VERSION: function() this.distribution.version
deleted file mode 100644
--- a/toolkit/content/tests/unit/test_updateChannelModule.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* 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/. */
-
-Components.utils.import("resource://gre/modules/Preferences.jsm");
-Components.utils.import("resource://gre/modules/UpdateChannel.jsm");
-
-const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
-const TEST_CHANNEL            = "TestChannel";
-const PREF_PARTNER_A          = "app.partner.test_partner_a";
-const TEST_PARTNER_A          = "TestPartnerA";
-const PREF_PARTNER_B          = "app.partner.test_partner_b";
-const TEST_PARTNER_B          = "TestPartnerB";
-
-function test_get() {
-  let defaultPrefs = new Preferences({ defaultBranch: true });
-  let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
-
-  do_check_eq(UpdateChannel.get(), currentChannel);
-  do_check_eq(UpdateChannel.get(false), currentChannel);
-
-  defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
-  do_check_eq(UpdateChannel.get(), TEST_CHANNEL);
-  do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
-
-  defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
-  defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
-  do_check_eq(UpdateChannel.get(),
-              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
-  do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
-}
-
-function run_test() {
-  test_get();
-}
--- a/toolkit/content/tests/unit/xpcshell.ini
+++ b/toolkit/content/tests/unit/xpcshell.ini
@@ -1,7 +1,6 @@
 [DEFAULT]
 head = 
 tail = 
 skip-if = toolkit == 'gonk'
 
 [test_contentAreaUtils.js]
-[test_updateChannelModule.js]
--- a/toolkit/modules/GMPInstallManager.jsm
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -25,34 +25,30 @@ var GMPInstallFailureReason = {
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://gre/modules/GMPUtils.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
 
 this.EXPORTED_SYMBOLS = ["GMPInstallManager", "GMPExtractor", "GMPDownloader",
                          "GMPAddon"];
 
-var gLocale = null;
-
 // Shared code for suppressing bad cert dialogs
 XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
   let temp = { };
   Cu.import("resource://gre/modules/CertUtils.jsm", temp);
   return temp;
 });
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 /**
  * Number of milliseconds after which we need to cancel `checkForAddons`.
  *
  * Bug 1087674 suggests that the XHR we use in `checkForAddons` may
  * never terminate in presence of network nuisances (e.g. strange
  * antivirus behavior). This timeout is a defensive measure to ensure
  * that we fail cleanly in such case.
@@ -60,148 +56,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 const CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS = 20000;
 
 function getScopedLogger(prefix) {
   // `PARENT_LOGGER_ID.` being passed here effectively links this logger
   // to the parentLogger.
   return Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", prefix + " ");
 }
 
-// This is copied directly from nsUpdateService.js
-// It is used for calculating the URL string w/ var replacement.
-// TODO: refactor this out somewhere else
-XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
-  let osVersion;
-  try {
-    osVersion = Services.sysinfo.getProperty("name") + " " +
-                Services.sysinfo.getProperty("version");
-  }
-  catch (e) {
-    LOG("gOSVersion - OS Version unknown: updates are not possible.");
-  }
-
-  if (osVersion) {
-    if (AppConstants.platform == "win") {
-      const BYTE = ctypes.uint8_t;
-      const WORD = ctypes.uint16_t;
-      const DWORD = ctypes.uint32_t;
-      const WCHAR = ctypes.char16_t;
-      const BOOL = ctypes.int;
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
-      const SZCSDVERSIONLENGTH = 128;
-      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
-          [
-          {dwOSVersionInfoSize: DWORD},
-          {dwMajorVersion: DWORD},
-          {dwMinorVersion: DWORD},
-          {dwBuildNumber: DWORD},
-          {dwPlatformId: DWORD},
-          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
-          {wServicePackMajor: WORD},
-          {wServicePackMinor: WORD},
-          {wSuiteMask: WORD},
-          {wProductType: BYTE},
-          {wReserved: BYTE}
-          ]);
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-          [
-          {wProcessorArchitecture: WORD},
-          {wReserved: WORD},
-          {dwPageSize: DWORD},
-          {lpMinimumApplicationAddress: ctypes.voidptr_t},
-          {lpMaximumApplicationAddress: ctypes.voidptr_t},
-          {dwActiveProcessorMask: DWORD.ptr},
-          {dwNumberOfProcessors: DWORD},
-          {dwProcessorType: DWORD},
-          {dwAllocationGranularity: DWORD},
-          {wProcessorLevel: WORD},
-          {wProcessorRevision: WORD}
-          ]);
-
-      let kernel32 = false;
-      try {
-        kernel32 = ctypes.open("Kernel32");
-      } catch (e) {
-        LOG("gOSVersion - Unable to open kernel32! " + e);
-        osVersion += ".unknown (unknown)";
-      }
-
-      if(kernel32) {
-        try {
-          // Get Service pack info
-          try {
-            let GetVersionEx = kernel32.declare("GetVersionExW",
-                                                ctypes.default_abi,
-                                                BOOL,
-                                                OSVERSIONINFOEXW.ptr);
-            let winVer = OSVERSIONINFOEXW();
-            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
-
-            if(0 !== GetVersionEx(winVer.address())) {
-              osVersion += "." + winVer.wServicePackMajor
-                        +  "." + winVer.wServicePackMinor;
-            } else {
-              LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
-              osVersion += ".unknown";
-            }
-          } catch (e) {
-            LOG("gOSVersion - error getting service pack information. Exception: " + e);
-            osVersion += ".unknown";
-          }
-
-          // Get processor architecture
-          let arch = "unknown";
-          try {
-            let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
-                                                       ctypes.default_abi,
-                                                       ctypes.void_t,
-                                                       SYSTEM_INFO.ptr);
-            let sysInfo = SYSTEM_INFO();
-            // Default to unknown
-            sysInfo.wProcessorArchitecture = 0xffff;
-
-            GetNativeSystemInfo(sysInfo.address());
-            switch(sysInfo.wProcessorArchitecture) {
-              case 9:
-                arch = "x64";
-                break;
-              case 6:
-                arch = "IA64";
-                break;
-              case 0:
-                arch = "x86";
-                break;
-            }
-          } catch (e) {
-            LOG("gOSVersion - error getting processor architecture.  Exception: " + e);
-          } finally {
-            osVersion += " (" + arch + ")";
-          }
-        } finally {
-          kernel32.close();
-        }
-      }
-    }
-
-    try {
-      osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
-    }
-    catch (e) {
-      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
-    }
-    osVersion = encodeURIComponent(osVersion);
-  }
-  return osVersion;
-});
-
 /**
  * Provides an easy API for downloading and installing GMP Addons
  */
 function GMPInstallManager() {
 }
 /**
  * Temp file name used for downloading
  */
@@ -216,34 +80,18 @@ GMPInstallManager.prototype = {
     let url = GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE);
     if (url) {
       log.info("Using override url: " + url);
     } else {
       url = GMPPrefs.get(GMPPrefs.KEY_URL);
       log.info("Using url: " + url);
     }
 
-    url =
-      url.replace(/%PRODUCT%/g, Services.appinfo.name)
-         .replace(/%VERSION%/g, Services.appinfo.version)
-         .replace(/%BUILD_ID%/g, Services.appinfo.appBuildID)
-         .replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + GMPUtils.ABI())
-         .replace(/%OS_VERSION%/g, gOSVersion);
-    if (/%LOCALE%/.test(url)) {
-      // TODO: Get the real local, does it actually matter for GMP plugins?
-      url = url.replace(/%LOCALE%/g, "en-US");
-    }
-    url =
-      url.replace(/%CHANNEL%/g, UpdateChannel.get())
-         .replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion)
-         .replace(/%DISTRIBUTION%/g,
-                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION))
-         .replace(/%DISTRIBUTION_VERSION%/g,
-                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION_VERSION))
-         .replace(/\+/g, "%2B");
+    url = UpdateUtils.formatUpdateURL(url);
+
     log.info("Using url (with replacement): " + url);
     return url;
   },
   /**
    * Performs an addon check.
    * @return a promise which will be resolved or rejected.
    *         The promise is resolved with an array of GMPAddons
    *         The promise is rejected with an object with properties:
@@ -903,17 +751,17 @@ GMPDownloader.prototype = {
         GMPPrefs.set(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
         // Reset the trial create pref, so that Gecko knows to do a test
         // run before reporting that the GMP works to content.
         GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_TRIAL_CREATE, gmpAddon.version,
                        gmpAddon.id);
         // Remember our ABI, so that if the profile is migrated to another
         // platform or from 32 -> 64 bit, we notice and don't try to load the
         // unexecutable plugin library.
-        GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), gmpAddon.id);
+        GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, gmpAddon.id);
         // Setting the version pref signals installation completion to consumers,
         // if you need to set other prefs etc. do it before this.
         GMPPrefs.set(GMPPrefs.KEY_PLUGIN_VERSION, gmpAddon.version,
                      gmpAddon.id);
         this._deferred.resolve(extractedPaths);
       }, err => {
         this._deferred.reject(err);
       });
--- a/toolkit/modules/GMPUtils.jsm
+++ b/toolkit/modules/GMPUtils.jsm
@@ -121,37 +121,16 @@ this.GMPUtils = {
     }
     this.reportedKeys.push(key);
 
     let hist = Services.telemetry.getHistogramById(key);
     if (hist) {
       hist.add(value);
     }
   },
-
-  ABI: function() {
-    // This is copied directly from nsUpdateService.js
-    let abi = null;
-    try {
-      abi = Services.appinfo.XPCOMABI;
-    }
-    catch (e) {
-      return "unknown";
-    }
-    if (AppConstants.platform == "macosx") {
-      // Mac universal build should report a different ABI than either macppc
-      // or mactel.
-      let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
-                     getService(Ci.nsIMacUtils);
-
-      if (macutils.isUniversalBinary)
-        abi += "-u-" + macutils.architecturesInBinary;
-    }
-    return abi;
-  }
 };
 
 /**
  * Manages preferences for GMP addons
  */
 this.GMPPrefs = {
   KEY_EME_ENABLED:              "media.eme.enabled",
   KEY_PLUGIN_ENABLED:           "media.{0}.enabled",
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -176,17 +176,17 @@ var dataProviders = {
       buildID: Services.appinfo.appBuildID,
       userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].
                  getService(Ci.nsIHttpProtocolHandler).
                  userAgent,
       safeMode: Services.appinfo.inSafeMode,
     };
 
     if (AppConstants.MOZ_UPDATER)
-      data.updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
+      data.updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
 
     try {
       data.vendor = Services.prefs.getCharPref("app.support.vendor");
     }
     catch (e) {}
     let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
                        getService(Ci.nsIURLFormatter);
     try {
deleted file mode 100644
--- a/toolkit/modules/UpdateChannel.jsm
+++ /dev/null
@@ -1,46 +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.EXPORTED_SYMBOLS = ["UpdateChannel"];
-
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-this.UpdateChannel = {
-  /**
-   * Read the update channel from defaults only.  We do this to ensure that
-   * the channel is tightly coupled with the application and does not apply
-   * to other instances of the application that may use the same profile.
-   *
-   * @param [optional] aIncludePartners
-   *        Whether or not to include the partner bits. Default: true.
-   */
-  get: function UpdateChannel_get(aIncludePartners = true) {
-    let channel = AppConstants.MOZ_UPDATE_CHANNEL;
-    let defaults = Services.prefs.getDefaultBranch(null);
-    try {
-      channel = defaults.getCharPref("app.update.channel");
-    } catch (e) {
-      // use default value when pref not found
-    }
-
-    if (aIncludePartners) {
-      try {
-        let partners = Services.prefs.getChildList("app.partner.").sort();
-        if (partners.length) {
-          channel += "-cck";
-          partners.forEach(function (prefName) {
-            channel += "-" + Services.prefs.getCharPref(prefName);
-          });
-        }
-      } catch (e) {
-        Cu.reportError(e);
-      }
-    }
-
-    return channel;
-  }
-};
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/UpdateUtils.jsm
@@ -0,0 +1,347 @@
+/* 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.EXPORTED_SYMBOLS = ["UpdateUtils"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+
+const FILE_UPDATE_LOCALE                  = "update.locale";
+const PREF_APP_DISTRIBUTION               = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
+const PREF_APP_B2G_VERSION                = "b2g.version";
+const PREF_APP_UPDATE_CUSTOM              = "app.update.custom";
+const PREF_APP_UPDATE_IMEI_HASH           = "app.update.imei_hash";
+
+
+this.UpdateUtils = {
+  /**
+   * Read the update channel from defaults only.  We do this to ensure that
+   * the channel is tightly coupled with the application and does not apply
+   * to other instances of the application that may use the same profile.
+   *
+   * @param [optional] aIncludePartners
+   *        Whether or not to include the partner bits. Default: true.
+   */
+  getUpdateChannel(aIncludePartners = true) {
+    let channel = AppConstants.MOZ_UPDATE_CHANNEL;
+    let defaults = Services.prefs.getDefaultBranch(null);
+    try {
+      channel = defaults.getCharPref("app.update.channel");
+    } catch (e) {
+      // use default value when pref not found
+    }
+
+    if (aIncludePartners) {
+      try {
+        let partners = Services.prefs.getChildList("app.partner.").sort();
+        if (partners.length) {
+          channel += "-cck";
+          partners.forEach(function (prefName) {
+            channel += "-" + Services.prefs.getCharPref(prefName);
+          });
+        }
+      } catch (e) {
+        Cu.reportError(e);
+      }
+    }
+
+    return channel;
+  },
+
+  get UpdateChannel() {
+    return this.getUpdateChannel();
+  },
+
+  /**
+   * Formats a URL by replacing %...% values with OS, build and locale specific
+   * values.
+   *
+   * @param  url
+   *         The URL to format.
+   * @return The formatted URL.
+   */
+  formatUpdateURL(url) {
+    url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
+    url = url.replace(/%VERSION%/g, Services.appinfo.version);
+    url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
+    url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI);
+    url = url.replace(/%OS_VERSION%/g, this.OSVersion);
+    if (/%LOCALE%/.test(url)) {
+      url = url.replace(/%LOCALE%/g, this.Locale);
+    }
+    url = url.replace(/%CHANNEL%/g, this.UpdateChannel);
+    url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
+    url = url.replace(/%DISTRIBUTION%/g,
+                      getDistributionPrefValue(PREF_APP_DISTRIBUTION));
+    url = url.replace(/%DISTRIBUTION_VERSION%/g,
+                      getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
+    url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, ""));
+    url = url.replace(/\+/g, "%2B");
+
+    if (AppConstants.platform == "gonk") {
+      let sysLibs = {};
+      Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
+      let productDevice = sysLibs.libcutils.property_get("ro.product.device");
+      let buildType = sysLibs.libcutils.property_get("ro.build.type");
+      url = url.replace(/%PRODUCT_MODEL%/g,
+                        sysLibs.libcutils.property_get("ro.product.model"));
+      if (buildType == "user" || buildType == "userdebug") {
+        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
+      } else {
+        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
+      }
+      url = url.replace(/%B2G_VERSION%/g,
+                        Preferences.get(PREF_APP_B2G_VERSION, null));
+      url = url.replace(/%IMEI%/g,
+                        getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
+    }
+
+    return url;
+  }
+};
+
+/* Get the distribution pref values, from defaults only */
+function getDistributionPrefValue(aPrefName) {
+  var prefValue = "default";
+
+  try {
+    prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
+  } catch (e) {
+    // use default when pref not found
+  }
+
+  return prefValue;
+}
+
+/**
+ * Gets the locale from the update.locale file for replacing %LOCALE% in the
+ * update url. The update.locale file can be located in the application
+ * directory or the GRE directory with preference given to it being located in
+ * the application directory.
+ */
+XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() {
+  let channel;
+  let locale;
+  for (let res of ['app', 'gre']) {
+    channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
+                                      null,
+                                      null,
+                                      null,      // aLoadingNode
+                                      Services.scriptSecurityManager.getSystemPrincipal(),
+                                      null,      // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
+    try {
+      let inputStream = channel.open();
+      locale = NetUtil.readInputStreamToString(inputStream, inputStream.available());
+    } catch(e) {}
+    if (locale)
+      return locale.trim();
+  }
+
+  Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
+                 "application or GRE directories");
+
+  return null;
+});
+
+/* Windows only getter that returns the processor architecture. */
+XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
+  // Get processor architecture
+  let arch = "unknown";
+
+  const WORD = ctypes.uint16_t;
+  const DWORD = ctypes.uint32_t;
+
+  // This structure is described at:
+  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+      [
+      {wProcessorArchitecture: WORD},
+      {wReserved: WORD},
+      {dwPageSize: DWORD},
+      {lpMinimumApplicationAddress: ctypes.voidptr_t},
+      {lpMaximumApplicationAddress: ctypes.voidptr_t},
+      {dwActiveProcessorMask: DWORD.ptr},
+      {dwNumberOfProcessors: DWORD},
+      {dwProcessorType: DWORD},
+      {dwAllocationGranularity: DWORD},
+      {wProcessorLevel: WORD},
+      {wProcessorRevision: WORD}
+      ]);
+
+  let kernel32 = false;
+  try {
+    kernel32 = ctypes.open("Kernel32");
+  } catch (e) {
+    Cu.reportError("Unable to open kernel32! Exception: " + e);
+  }
+
+  if (kernel32) {
+    try {
+      let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+                                                 ctypes.default_abi,
+                                                 ctypes.void_t,
+                                                 SYSTEM_INFO.ptr);
+      let winSystemInfo = SYSTEM_INFO();
+      // Default to unknown
+      winSystemInfo.wProcessorArchitecture = 0xffff;
+
+      GetNativeSystemInfo(winSystemInfo.address());
+      switch (winSystemInfo.wProcessorArchitecture) {
+        case 9:
+          arch = "x64";
+          break;
+        case 6:
+          arch = "IA64";
+          break;
+        case 0:
+          arch = "x86";
+          break;
+      }
+    } catch (e) {
+      Cu.reportError("Error getting processor architecture. " +
+                     "Exception: " + e);
+    } finally {
+      kernel32.close();
+    }
+  }
+
+  return arch;
+});
+
+XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() {
+  let abi = null;
+  try {
+    abi = Services.appinfo.XPCOMABI;
+  }
+  catch (e) {
+    Cu.reportError("XPCOM ABI unknown");
+  }
+
+  if (AppConstants.platform == "macosx") {
+    // Mac universal build should report a different ABI than either macppc
+    // or mactel.
+    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+                   getService(Ci.nsIMacUtils);
+
+    if (macutils.isUniversalBinary) {
+      abi += "-u-" + macutils.architecturesInBinary;
+    }
+  } else if (AppConstants.platform == "win") {
+    // Windows build should report the CPU architecture that it's running on.
+    abi += "-" + gWinCPUArch;
+  }
+  return abi;
+});
+
+XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() {
+  let osVersion;
+  try {
+    osVersion = Services.sysinfo.getProperty("name") + " " +
+                Services.sysinfo.getProperty("version");
+  }
+  catch (e) {
+    Cu.reportError("OS Version unknown.");
+  }
+
+  if (osVersion) {
+    if (AppConstants.platform == "win") {
+      const BYTE = ctypes.uint8_t;
+      const WORD = ctypes.uint16_t;
+      const DWORD = ctypes.uint32_t;
+      const WCHAR = ctypes.char16_t;
+      const BOOL = ctypes.int;
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+      const SZCSDVERSIONLENGTH = 128;
+      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+          [
+          {dwOSVersionInfoSize: DWORD},
+          {dwMajorVersion: DWORD},
+          {dwMinorVersion: DWORD},
+          {dwBuildNumber: DWORD},
+          {dwPlatformId: DWORD},
+          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+          {wServicePackMajor: WORD},
+          {wServicePackMinor: WORD},
+          {wSuiteMask: WORD},
+          {wProductType: BYTE},
+          {wReserved: BYTE}
+          ]);
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+          [
+          {wProcessorArchitecture: WORD},
+          {wReserved: WORD},
+          {dwPageSize: DWORD},
+          {lpMinimumApplicationAddress: ctypes.voidptr_t},
+          {lpMaximumApplicationAddress: ctypes.voidptr_t},
+          {dwActiveProcessorMask: DWORD.ptr},
+          {dwNumberOfProcessors: DWORD},
+          {dwProcessorType: DWORD},
+          {dwAllocationGranularity: DWORD},
+          {wProcessorLevel: WORD},
+          {wProcessorRevision: WORD}
+          ]);
+
+      let kernel32 = false;
+      try {
+        kernel32 = ctypes.open("Kernel32");
+      } catch (e) {
+        Cu.reportError("Unable to open kernel32! " + e);
+        osVersion += ".unknown (unknown)";
+      }
+
+      if (kernel32) {
+        try {
+          // Get Service pack info
+          try {
+            let GetVersionEx = kernel32.declare("GetVersionExW",
+                                                ctypes.default_abi,
+                                                BOOL,
+                                                OSVERSIONINFOEXW.ptr);
+            let winVer = OSVERSIONINFOEXW();
+            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+            if(0 !== GetVersionEx(winVer.address())) {
+              osVersion += "." + winVer.wServicePackMajor +
+                           "." + winVer.wServicePackMinor;
+            } else {
+              Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
+              osVersion += ".unknown";
+            }
+          } catch (e) {
+            Cu.reportError("Error getting service pack information. Exception: " + e);
+            osVersion += ".unknown";
+          }
+        } finally {
+          kernel32.close();
+        }
+
+        // Add processor architecture
+        osVersion += " (" + gWinCPUArch + ")";
+      }
+    }
+
+    try {
+      osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
+    }
+    catch (e) {
+      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
+    }
+    osVersion = encodeURIComponent(osVersion);
+  }
+  return osVersion;
+});
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -70,17 +70,17 @@ EXTRA_JS_MODULES += [
     'ShortcutUtils.jsm',
     'Sntp.jsm',
     'SpatialNavigation.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'TelemetryTimestamps.jsm',
     'Timer.jsm',
     'Troubleshoot.jsm',
-    'UpdateChannel.jsm',
+    'UpdateUtils.jsm',
     'WebChannel.jsm',
     'WindowDraggingUtils.jsm',
     'ZipUtils.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'AppConstants.jsm',
     'SessionRecorder.jsm',
--- a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -6,16 +6,17 @@ const URL_HOST = "http://localhost";
 
 var GMPScope = Cu.import("resource://gre/modules/GMPInstallManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm")
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 do_get_profile();
 
 function run_test() {Cu.import("resource://gre/modules/Preferences.jsm")
   Preferences.set("media.gmp.log.dump", true);
   Preferences.set("media.gmp.log.level", 0);
   run_next_test();
 }
@@ -485,17 +486,17 @@ function* test_checkForAddons_installAdd
     // Make sure the prefs are set correctly
     do_check_true(!!GMPScope.GMPPrefs.get(
       GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id));
     do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "",
                                       gmpAddon.id),
                 "1.1");
     do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, "",
                                       gmpAddon.id),
-                GMPScope.GMPUtils.ABI());
+                UpdateUtils.ABI);
     // Make sure it reports as being installed
     do_check_true(gmpAddon.isInstalled);
 
     // Cleanup
     extractedFile.parent.remove(true);
     zipFile.remove(false);
     httpServer.stop(function() {});
     do_print("Removing downloaded GMP file: " + downloadedGMPFile.path);
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+const { utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+
+const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
+const TEST_CHANNEL            = "TestChannel";
+const PREF_PARTNER_A          = "app.partner.test_partner_a";
+const TEST_PARTNER_A          = "TestPartnerA";
+const PREF_PARTNER_B          = "app.partner.test_partner_b";
+const TEST_PARTNER_B          = "TestPartnerB";
+
+add_task(function* test_updatechannel() {
+  let defaultPrefs = new Preferences({ defaultBranch: true });
+  let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
+
+  do_check_eq(UpdateUtils.UpdateChannel, currentChannel);
+  do_check_eq(UpdateUtils.getUpdateChannel(true), currentChannel);
+  do_check_eq(UpdateUtils.getUpdateChannel(false), currentChannel);
+
+  defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
+  do_check_eq(UpdateUtils.UpdateChannel, TEST_CHANNEL);
+  do_check_eq(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL);
+  do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
+
+  defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
+  defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
+  do_check_eq(UpdateUtils.UpdateChannel,
+              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
+  do_check_eq(UpdateUtils.getUpdateChannel(true),
+              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
+  do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
@@ -0,0 +1,295 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://testing-common/AppInfo.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+
+const PREF_APP_UPDATE_CHANNEL     = "app.update.channel";
+const PREF_APP_PARTNER_BRANCH     = "app.partner.";
+const PREF_DISTRIBUTION_ID        = "distribution.id";
+const PREF_DISTRIBUTION_VERSION   = "distribution.version";
+
+const URL_PREFIX = "http://localhost/";
+
+const MSG_SHOULD_EQUAL = " should equal the expected value";
+
+// For INSTALL_LOCALE
+load("../../../mozapps/update/tests/data/xpcshellConstantsPP.js");
+
+updateAppInfo();
+const gAppInfo = getAppInfo();
+const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null);
+
+function setUpdateChannel(aChannel) {
+  gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel);
+}
+
+function getServicePack() {
+  // NOTE: This function is a helper function and not a test.  Thus,
+  // it uses throw() instead of do_throw().  Any tests that use this function
+  // should catch exceptions thrown in this function and deal with them
+  // appropriately (usually by calling do_throw).
+  const BYTE = ctypes.uint8_t;
+  const WORD = ctypes.uint16_t;
+  const DWORD = ctypes.uint32_t;
+  const WCHAR = ctypes.char16_t;
+  const BOOL = ctypes.int;
+
+  // This structure is described at:
+  // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+  const SZCSDVERSIONLENGTH = 128;
+  const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+      [
+      {dwOSVersionInfoSize: DWORD},
+      {dwMajorVersion: DWORD},
+      {dwMinorVersion: DWORD},
+      {dwBuildNumber: DWORD},
+      {dwPlatformId: DWORD},
+      {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+      {wServicePackMajor: WORD},
+      {wServicePackMinor: WORD},
+      {wSuiteMask: WORD},
+      {wProductType: BYTE},
+      {wReserved: BYTE}
+      ]);
+
+  let kernel32 = ctypes.open("kernel32");
+  try {
+    let GetVersionEx = kernel32.declare("GetVersionExW",
+                                        ctypes.default_abi,
+                                        BOOL,
+                                        OSVERSIONINFOEXW.ptr);
+    let winVer = OSVERSIONINFOEXW();
+    winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+    if (0 === GetVersionEx(winVer.address())) {
+      // Using "throw" instead of "do_throw" (see NOTE above)
+      throw("Failure in GetVersionEx (returned 0)");
+    }
+
+    return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
+  } finally {
+    kernel32.close();
+  }
+}
+
+function getProcArchitecture() {
+  // NOTE: This function is a helper function and not a test.  Thus,
+  // it uses throw() instead of do_throw().  Any tests that use this function
+  // should catch exceptions thrown in this function and deal with them
+  // appropriately (usually by calling do_throw).
+  const WORD = ctypes.uint16_t;
+  const DWORD = ctypes.uint32_t;
+
+  // This structure is described at:
+  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+      [
+      {wProcessorArchitecture: WORD},
+      {wReserved: WORD},
+      {dwPageSize: DWORD},
+      {lpMinimumApplicationAddress: ctypes.voidptr_t},
+      {lpMaximumApplicationAddress: ctypes.voidptr_t},
+      {dwActiveProcessorMask: DWORD.ptr},
+      {dwNumberOfProcessors: DWORD},
+      {dwProcessorType: DWORD},
+      {dwAllocationGranularity: DWORD},
+      {wProcessorLevel: WORD},
+      {wProcessorRevision: WORD}
+      ]);
+
+  let kernel32 = ctypes.open("kernel32");
+  try {
+    let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+                                               ctypes.default_abi,
+                                               ctypes.void_t,
+                                               SYSTEM_INFO.ptr);
+    let sysInfo = SYSTEM_INFO();
+    // Default to unknown
+    sysInfo.wProcessorArchitecture = 0xffff;
+
+    GetNativeSystemInfo(sysInfo.address());
+    switch(sysInfo.wProcessorArchitecture) {
+      case 9:
+        return "x64";
+      case 6:
+        return "IA64";
+      case 0:
+        return "x86";
+      default:
+        // Using "throw" instead of "do_throw" (see NOTE above)
+        throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
+    }
+  } finally {
+    kernel32.close();
+  }
+}
+
+// Helper function for formatting a url and getting the result we're
+// interested in
+function getResult(url) {
+  url = UpdateUtils.formatUpdateURL(url);
+  return url.substr(URL_PREFIX.length).split("/")[0];
+}
+
+// url constructed with %PRODUCT%
+add_task(function* test_product() {
+  let url = URL_PREFIX + "%PRODUCT%/";
+  Assert.equal(getResult(url), gAppInfo.name,
+               "the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %VERSION%
+add_task(function* test_version() {
+  let url = URL_PREFIX + "%VERSION%/";
+  Assert.equal(getResult(url), gAppInfo.version,
+               "the url param for %VERSION%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %BUILD_ID%
+add_task(function* test_build_id() {
+  let url = URL_PREFIX + "%BUILD_ID%/";
+  Assert.equal(getResult(url), gAppInfo.appBuildID,
+               "the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %BUILD_TARGET%
+// XXX TODO - it might be nice if we tested the actual ABI
+add_task(function* test_build_target() {
+  let url = URL_PREFIX + "%BUILD_TARGET%/";
+
+  let abi;
+  try {
+    abi = gAppInfo.XPCOMABI;
+  } catch (e) {
+    do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
+  }
+
+  if (AppConstants.platform == "macosx") {
+    // Mac universal build should report a different ABI than either macppc
+    // or mactel. This is necessary since nsUpdateService.js will set the ABI to
+    // Universal-gcc3 for Mac universal builds.
+    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+                   getService(Ci.nsIMacUtils);
+
+    if (macutils.isUniversalBinary) {
+      abi += "-u-" + macutils.architecturesInBinary;
+    }
+  } else if (IS_WIN) {
+    // Windows build should report the CPU architecture that it's running on.
+    abi += "-" + getProcArchitecture();
+  }
+
+  Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
+               "the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %LOCALE%
+// Bug 488936 added the update.locale file that stores the update locale
+add_task(function* test_locale() {
+  // The code that gets the locale accesses the profile which is only available
+  // after calling do_get_profile in xpcshell tests. This prevents an error from
+  // being logged.
+  do_get_profile();
+
+  let url = URL_PREFIX + "%LOCALE%/";
+  Assert.equal(getResult(url), INSTALL_LOCALE,
+               "the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %CHANNEL%
+add_task(function* test_channel() {
+  let url = URL_PREFIX + "%CHANNEL%/";
+  setUpdateChannel("test_channel");
+  Assert.equal(getResult(url), "test_channel",
+               "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %CHANNEL% with distribution partners
+add_task(function* test_channel_distribution() {
+  let url = URL_PREFIX + "%CHANNEL%/";
+  gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
+                                 "test_partner1");
+  gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
+                                 "test_partner2");
+  Assert.equal(getResult(url),
+               "test_channel-cck-test_partner1-test_partner2",
+               "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %PLATFORM_VERSION%
+add_task(function* test_platform_version() {
+  let url = URL_PREFIX + "%PLATFORM_VERSION%/";
+  Assert.equal(getResult(url), gAppInfo.platformVersion,
+               "the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %OS_VERSION%
+add_task(function* test_os_version() {
+  let url = URL_PREFIX + "%OS_VERSION%/";
+  let osVersion;
+  let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+  osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+
+  if (IS_WIN) {
+    try {
+      let servicePack = getServicePack();
+      osVersion += "." + servicePack;
+    } catch (e) {
+      do_throw("Failure obtaining service pack: " + e);
+    }
+
+    if ("5.0" === sysInfo.getProperty("version")) { // Win2K
+      osVersion += " (unknown)";
+    } else {
+      try {
+        osVersion += " (" + getProcArchitecture() + ")";
+      } catch (e) {
+        do_throw("Failed to obtain processor architecture: " + e);
+      }
+    }
+  }
+
+  if (osVersion) {
+    try {
+      osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+    } catch (e) {
+      // Not all platforms have a secondary widget library, so an error is
+      // nothing to worry about.
+    }
+    osVersion = encodeURIComponent(osVersion);
+  }
+
+  Assert.equal(getResult(url), osVersion,
+               "the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %DISTRIBUTION%
+add_task(function* test_distribution() {
+  let url = URL_PREFIX + "%DISTRIBUTION%/";
+  gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
+  Assert.equal(getResult(url), "test_distro",
+               "the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
+});
+
+// url constructed with %DISTRIBUTION_VERSION%
+add_task(function* test_distribution_version() {
+  let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
+  gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
+  Assert.equal(getResult(url), "test_distro_version",
+               "the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
+});
+
+add_task(function* test_custom() {
+  Services.prefs.setCharPref("app.update.custom", "custom");
+  let url = URL_PREFIX + "%CUSTOM%/";
+  Assert.equal(getResult(url), "custom",
+               "the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
+});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -52,12 +52,14 @@ skip-if = toolkit == 'android'
 [test_sqlite_shutdown.js]
 skip-if = toolkit == 'android'
 [test_task.js]
 skip-if = toolkit == 'android'
 [test_TelemetryTimestamps.js]
 skip-if = toolkit == 'android'
 [test_timer.js]
 skip-if = toolkit == 'android'
+[test_UpdateUtils_url.js]
+[test_UpdateUtils_updatechannel.js]
 [test_web_channel.js]
 [test_web_channel_broker.js]
 [test_ZipUtils.js]
 skip-if = toolkit == 'android'
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -14,16 +14,17 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/GMPUtils.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(
   this, "setTimeout", "resource://gre/modules/Timer.jsm");
 
 const URI_EXTENSION_STRINGS  = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME       = "type.%ID%.name";
@@ -485,18 +486,18 @@ GMPWrapper.prototype = {
   },
 
   validate: function() {
     if (!this.isInstalled) {
       // Not installed -> Valid.
       return { installed: false, valid: true };
     }
 
-    let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), this._plugin.id);
-    if (abi != GMPUtils.ABI()) {
+    let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, this._plugin.id);
+    if (abi != UpdateUtils.ABI) {
       // ABI doesn't match. Possibly this is a profile migrated across platforms
       // or from 32 -> 64 bit.
       return {
         installed: true,
         mismatchedABI: true,
         valid: false
       };
     }
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -18,18 +18,18 @@ try {
   // process. We're used in the child process (for now), so guard against
   // this.
   Components.utils.import("resource://gre/modules/AddonManager.jsm");
 } catch (e) {
 }
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
@@ -554,17 +554,17 @@ Blocklist.prototype = {
     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
     // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
     if (gApp.version)
       dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
-    dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
+    dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel);
     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
     dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
     dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
     dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 var GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
   () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
 var gMockAddons = new Map();
@@ -333,17 +334,17 @@ add_task(function* test_pluginRegistrati
     yield promiseRestartManager();
     Assert.equal(addedPaths.indexOf(file.path), -1);
     Assert.deepEqual(removedPaths, [file.path]);
 
     // Setting the ABI to expected ABI should cause registration at startup.
     clearPaths();
     gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
                        TEST_VERSION);
-    gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), GMPScope.GMPUtils.ABI());
+    gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI);
     yield promiseRestartManager();
     Assert.notEqual(addedPaths.indexOf(file.path), -1);
     Assert.deepEqual(removedPaths, []);
 
     // Check that clearing the version doesn't trigger registration.
     clearPaths();
     gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
     Assert.deepEqual(addedPaths, []);
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -25,18 +25,16 @@ const PREF_APP_UPDATE_BACKGROUND_INTERVA
 const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CANCELATIONS        = "app.update.cancelations";
 const PREF_APP_UPDATE_CERTS_BRANCH        = "app.update.certs.";
 const PREF_APP_UPDATE_CERT_CHECKATTRS     = "app.update.cert.checkAttributes";
 const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
 const PREF_APP_UPDATE_CERT_MAXERRORS      = "app.update.cert.maxErrors";
 const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
-const PREF_APP_UPDATE_CUSTOM              = "app.update.custom";
-const PREF_APP_UPDATE_IMEI_HASH           = "app.update.imei_hash";
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
 const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
 const PREF_APP_UPDATE_INCOMPATIBLE_MODE   = "app.update.incompatible.mode";
 const PREF_APP_UPDATE_INTERVAL            = "app.update.interval";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_MODE                = "app.update.mode";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
@@ -49,21 +47,16 @@ const PREF_APP_UPDATE_URL               
 const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
 const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
 const PREF_APP_UPDATE_SERVICE_ENABLED     = "app.update.service.enabled";
 const PREF_APP_UPDATE_SERVICE_ERRORS      = "app.update.service.errors";
 const PREF_APP_UPDATE_SERVICE_MAX_ERRORS  = "app.update.service.maxErrors";
 const PREF_APP_UPDATE_SOCKET_ERRORS       = "app.update.socket.maxErrors";
 const PREF_APP_UPDATE_RETRY_TIMEOUT       = "app.update.socket.retryTimeout";
 
-const PREF_APP_DISTRIBUTION               = "distribution.id";
-const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
-
-const PREF_APP_B2G_VERSION                = "b2g.version";
-
 const PREF_EM_HOTFIX_ID                   = "extensions.hotfix.id";
 
 const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
 const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul";
 const URI_BRAND_PROPERTIES      = "chrome://branding/locale/brand.properties";
 const URI_UPDATES_PROPERTIES    = "chrome://mozapps/locale/update/updates.properties";
 const URI_UPDATE_NS             = "http://www.mozilla.org/2005/app-update";
 
@@ -82,17 +75,16 @@ const FILE_UPDATE_VERSION = "update.vers
 const FILE_UPDATE_ARCHIVE = "update.mar";
 const FILE_UPDATE_LINK    = "update.link";
 const FILE_UPDATE_LOG     = "update.log";
 const FILE_UPDATES_DB     = "updates.xml";
 const FILE_UPDATE_ACTIVE  = "active-update.xml";
 const FILE_PERMS_TEST     = "update.test";
 const FILE_LAST_LOG       = "last-update.log";
 const FILE_BACKUP_LOG     = "backup-update.log";
-const FILE_UPDATE_LOCALE  = "update.locale";
 
 const STATE_NONE            = "null";
 const STATE_DOWNLOADING     = "downloading";
 const STATE_PENDING         = "pending";
 const STATE_PENDING_SVC     = "pending-service";
 const STATE_APPLYING        = "applying";
 const STATE_APPLIED         = "applied";
 const STATE_APPLIED_OS      = "applied-os";
@@ -205,227 +197,34 @@ var gSDCardMountLock = null;
 // Gonk only
 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
   if (AppConstants.platform != "gonk") {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }
   return Services.env.get("EXTERNAL_STORAGE");
 });
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
-                                  "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
   return Services.strings.createBundle(URI_UPDATES_PROPERTIES);
 });
 
 // shared code for suppressing bad cert dialogs
 XPCOMUtils.defineLazyGetter(this, "gCertUtils", function aus_gCertUtils() {
   let temp = { };
   Cu.import("resource://gre/modules/CertUtils.jsm", temp);
   return temp;
 });
 
-XPCOMUtils.defineLazyGetter(this, "gABI", function aus_gABI() {
-  let abi = null;
-  try {
-    abi = Services.appinfo.XPCOMABI;
-  }
-  catch (e) {
-    LOG("gABI - XPCOM ABI unknown: updates are not possible.");
-  }
-
-  if (AppConstants.platform == "macosx") {
-    // Mac universal build should report a different ABI than either macppc
-    // or mactel.
-    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
-                   getService(Ci.nsIMacUtils);
-
-    if (macutils.isUniversalBinary) {
-      abi += "-u-" + macutils.architecturesInBinary;
-    }
-  } else if (AppConstants.platform == "win") {
-    // Windows build should report the CPU architecture that it's running on.
-    abi += "-" + gWinCPUArch;
-  }
-  return abi;
-});
-
-XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
-  let osVersion;
-  try {
-    osVersion = Services.sysinfo.getProperty("name") + " " +
-                Services.sysinfo.getProperty("version");
-  }
-  catch (e) {
-    LOG("gOSVersion - OS Version unknown: updates are not possible.");
-  }
-
-  if (osVersion) {
-    if (AppConstants.platform == "win") {
-      const BYTE = ctypes.uint8_t;
-      const WORD = ctypes.uint16_t;
-      const DWORD = ctypes.uint32_t;
-      const WCHAR = ctypes.char16_t;
-      const BOOL = ctypes.int;
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
-      const SZCSDVERSIONLENGTH = 128;
-      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
-          [
-          {dwOSVersionInfoSize: DWORD},
-          {dwMajorVersion: DWORD},
-          {dwMinorVersion: DWORD},
-          {dwBuildNumber: DWORD},
-          {dwPlatformId: DWORD},
-          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
-          {wServicePackMajor: WORD},
-          {wServicePackMinor: WORD},
-          {wSuiteMask: WORD},
-          {wProductType: BYTE},
-          {wReserved: BYTE}
-          ]);
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-          [
-          {wProcessorArchitecture: WORD},
-          {wReserved: WORD},
-          {dwPageSize: DWORD},
-          {lpMinimumApplicationAddress: ctypes.voidptr_t},
-          {lpMaximumApplicationAddress: ctypes.voidptr_t},
-          {dwActiveProcessorMask: DWORD.ptr},
-          {dwNumberOfProcessors: DWORD},
-          {dwProcessorType: DWORD},
-          {dwAllocationGranularity: DWORD},
-          {wProcessorLevel: WORD},
-          {wProcessorRevision: WORD}
-          ]);
-
-      let kernel32 = false;
-      try {
-        kernel32 = ctypes.open("Kernel32");
-      } catch (e) {
-        LOG("gOSVersion - Unable to open kernel32! " + e);
-        osVersion += ".unknown (unknown)";
-      }
-
-      if (kernel32) {
-        try {
-          // Get Service pack info
-          try {
-            let GetVersionEx = kernel32.declare("GetVersionExW",
-                                                ctypes.default_abi,
-                                                BOOL,
-                                                OSVERSIONINFOEXW.ptr);
-            let winVer = OSVERSIONINFOEXW();
-            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
-
-            if(0 !== GetVersionEx(winVer.address())) {
-              osVersion += "." + winVer.wServicePackMajor +
-                           "." + winVer.wServicePackMinor;
-            } else {
-              LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
-              osVersion += ".unknown";
-            }
-          } catch (e) {
-            LOG("gOSVersion - error getting service pack information. Exception: " + e);
-            osVersion += ".unknown";
-          }
-        } finally {
-          kernel32.close();
-        }
-
-        // Add processor architecture
-        osVersion += " (" + gWinCPUArch + ")";
-      }
-    }
-
-    try {
-      osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
-    }
-    catch (e) {
-      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
-    }
-    osVersion = encodeURIComponent(osVersion);
-  }
-  return osVersion;
-});
-
-/* Windows only getter that returns the processor architecture. */
-XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
-  // Get processor architecture
-  let arch = "unknown";
-
-  const WORD = ctypes.uint16_t;
-  const DWORD = ctypes.uint32_t;
-
-  // This structure is described at:
-  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-      [
-      {wProcessorArchitecture: WORD},
-      {wReserved: WORD},
-      {dwPageSize: DWORD},
-      {lpMinimumApplicationAddress: ctypes.voidptr_t},
-      {lpMaximumApplicationAddress: ctypes.voidptr_t},
-      {dwActiveProcessorMask: DWORD.ptr},
-      {dwNumberOfProcessors: DWORD},
-      {dwProcessorType: DWORD},
-      {dwAllocationGranularity: DWORD},
-      {wProcessorLevel: WORD},
-      {wProcessorRevision: WORD}
-      ]);
-
-  let kernel32 = false;
-  try {
-    kernel32 = ctypes.open("Kernel32");
-  } catch (e) {
-    LOG("gWinCPUArch - Unable to open kernel32! Exception: " + e);
-  }
-
-  if (kernel32) {
-    try {
-      let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
-                                                 ctypes.default_abi,
-                                                 ctypes.void_t,
-                                                 SYSTEM_INFO.ptr);
-      let winSystemInfo = SYSTEM_INFO();
-      // Default to unknown
-      winSystemInfo.wProcessorArchitecture = 0xffff;
-
-      GetNativeSystemInfo(winSystemInfo.address());
-      switch (winSystemInfo.wProcessorArchitecture) {
-        case 9:
-          arch = "x64";
-          break;
-        case 6:
-          arch = "IA64";
-          break;
-        case 0:
-          arch = "x86";
-          break;
-      }
-    } catch (e) {
-      LOG("gWinCPUArch - error getting processor architecture. " +
-          "Exception: " + e);
-    } finally {
-      kernel32.close();
-    }
-  }
-
-  return arch;
-});
-
 /**
  * Tests to make sure that we can write to a given directory.
  *
  * @param updateTestFile a test file in the directory that needs to be tested.
  * @param createDirectory whether a test directory should be created.
  * @throws if we don't have right access to the directory.
  */
 function testWriteAccess(updateTestFile, createDirectory) {
@@ -722,23 +521,23 @@ XPCOMUtils.defineLazyGetter(this, "gCanC
   var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
   if (!enabled && Services.prefs.prefIsLocked(PREF_APP_UPDATE_ENABLED)) {
     LOG("gCanCheckForUpdates - unable to automatically check for updates, " +
         "the preference is disabled and admistratively locked.");
     return false;
   }
 
   // If we don't know the binary platform we're updating, we can't update.
-  if (!gABI) {
+  if (!UpdateUtils.ABI) {
     LOG("gCanCheckForUpdates - unable to check for updates, unknown ABI");
     return false;
   }
 
   // If we don't know the OS version we're updating, we can't update.
-  if (!gOSVersion) {
+  if (!UpdateUtils.OSVersion) {
     LOG("gCanCheckForUpdates - unable to check for updates, unknown OS " +
         "version");
     return false;
   }
 
   LOG("gCanCheckForUpdates - able to check for updates");
   return true;
 });
@@ -1238,68 +1037,16 @@ function cleanupActiveUpdate() {
   um.activeUpdate = null;
   um.saveUpdates();
 
   // Now trash the updates directory, since we're done with it
   cleanUpUpdatesDir();
 }
 
 /**
- * Gets the locale from the update.locale file for replacing %LOCALE% in the
- * update url. The update.locale file can be located in the application
- * directory or the GRE directory with preference given to it being located in
- * the application directory.
- */
-function getLocale() {
-  if (gLocale) {
-    return gLocale;
-  }
-
-  let channel;
-  for (let res of ['app', 'gre']) {
-    channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
-                                      null,
-                                      null,
-                                      null,      // aLoadingNode
-                                      Services.scriptSecurityManager.getSystemPrincipal(),
-                                      null,      // aTriggeringPrincipal
-                                      Ci.nsILoadInfo.SEC_NORMAL,
-                                      Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
-    try {
-      var inputStream = channel.open();
-      gLocale = readStringFromInputStream(inputStream);
-    } catch(e) {}
-    if (gLocale)
-      break;
-  }
-
-  if (!gLocale)
-    throw Components.Exception(FILE_UPDATE_LOCALE + " file doesn't exist in " +
-                               "either the application or GRE directories",
-                               Cr.NS_ERROR_FILE_NOT_FOUND);
-
-  LOG("getLocale - getting locale from file: " + channel.originalURI.spec +
-      ", locale: " + gLocale);
-  return gLocale;
-}
-
-/* Get the distribution pref values, from defaults only */
-function getDistributionPrefValue(aPrefName) {
-  var prefValue = "default";
-
-  try {
-    prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
-  } catch (e) {
-    // use default when pref not found
-  }
-
-  return prefValue;
-}
-
-/**
  * An enumeration of items in a JS array.
  * @constructor
  */
 function ArrayEnumerator(aItems) {
   this._index = 0;
   if (aItems) {
     for (var i = 0; i < aItems.length; ++i) {
       if (!aItems[i])
@@ -2522,19 +2269,19 @@ UpdateService.prototype = {
     let validUpdateURL = true;
     try {
       this.backgroundChecker.getUpdateURL(false);
     } catch (e) {
       validUpdateURL = false;
     }
     // The following checks are done here so they can be differentiated from
     // foreground checks.
-    if (!gOSVersion) {
+    if (!UpdateUtils.OSVersion) {
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION);
-    } else if (!gABI) {
+    } else if (!UpdateUtils.ABI) {
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI);
     } else if (!validUpdateURL) {
       if (overridePrefHasValue) {
         if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE)) {
           AUSTLMY.pingCheckCode(this._pingSuffix,
                                 AUSTLMY.CHK_INVALID_USER_OVERRIDE_URL);
         } else {
           AUSTLMY.pingCheckCode(this._pingSuffix,
@@ -3266,24 +3013,24 @@ UpdateManager.prototype = {
     return this._updates.length;
   },
 
   /**
    * See nsIUpdateService.idl
    */
   get activeUpdate() {
     if (this._activeUpdate &&
-        this._activeUpdate.channel != UpdateChannel.get()) {
+        this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
       LOG("UpdateManager:get activeUpdate - channel has changed, " +
           "reloading default preferences to workaround bug 802022");
       // Workaround to get distribution preferences loaded (Bug 774618). This
       // can be removed after bug 802022 is fixed.
       let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver);
       prefSvc.observe(null, "reload-default-prefs", null);
-      if (this._activeUpdate.channel != UpdateChannel.get()) {
+      if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
         // User switched channels, clear out any old active updates and remove
         // partial downloads
         this._activeUpdate = null;
         this.saveUpdates();
 
         // Destroy the updates directory, since we're done with it.
         cleanUpUpdatesDir();
       }
@@ -3511,50 +3258,17 @@ Checker.prototype = {
       }
     }
 
     if (!url || url == "") {
       LOG("Checker:getUpdateURL - update URL not defined");
       return null;
     }
 
-    url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
-    url = url.replace(/%VERSION%/g, Services.appinfo.version);
-    url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
-    url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + gABI);
-    url = url.replace(/%OS_VERSION%/g, gOSVersion);
-    if (/%LOCALE%/.test(url)) {
-      url = url.replace(/%LOCALE%/g, getLocale());
-    }
-    url = url.replace(/%CHANNEL%/g, UpdateChannel.get());
-    url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
-    url = url.replace(/%DISTRIBUTION%/g,
-                      getDistributionPrefValue(PREF_APP_DISTRIBUTION));
-    url = url.replace(/%DISTRIBUTION_VERSION%/g,
-                      getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
-    url = url.replace(/%CUSTOM%/g, getPref("getCharPref", PREF_APP_UPDATE_CUSTOM, ""));
-    url = url.replace(/\+/g, "%2B");
-
-    if (AppConstants.platform == "gonk") {
-      let sysLibs = {};
-      Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
-      let productDevice = sysLibs.libcutils.property_get("ro.product.device");
-      let buildType = sysLibs.libcutils.property_get("ro.build.type");
-      url = url.replace(/%PRODUCT_MODEL%/g,
-                        sysLibs.libcutils.property_get("ro.product.model"));
-      if (buildType == "user" || buildType == "userdebug") {
-        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
-      } else {
-        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
-      }
-      url = url.replace(/%B2G_VERSION%/g,
-                        getPref("getCharPref", PREF_APP_B2G_VERSION, null));
-      url = url.replace(/%IMEI%/g,
-                        getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
-    }
+    url = UpdateUtils.formatUpdateURL(url);
 
     if (force) {
       url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
     }
 
     LOG("Checker:getUpdateURL - update URL: " + url);
     return url;
   },
@@ -3636,17 +3350,17 @@ Checker.prototype = {
       let update;
       try {
         update = new Update(updateElement);
       } catch (e) {
         LOG("Checker:_updates get - invalid <update/>, ignoring...");
         continue;
       }
       update.serviceURL = this.getUpdateURL(this._forced);
-      update.channel = UpdateChannel.get();
+      update.channel = UpdateUtils.UpdateChannel;
       updates.push(update);
     }
 
     return updates;
   },
 
   /**
    * Returns the status code for the XMLHttpRequest