Bug 792992 - Make update URL configurable via pref. r=snorp, a=lmandel
authorEugen Sawin <esawin@mozilla.com>
Tue, 03 Feb 2015 17:18:13 +0100
changeset 250253 ae511f0dda0f
parent 250252 f9f0120c1adf
child 250254 7113cd46019c
push id4527
push userryanvm@gmail.com
push date2015-03-05 16:09 +0000
treeherdermozilla-beta@00bad6e2ffbc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, lmandel
bugs792992
milestone37.0
Bug 792992 - Make update URL configurable via pref. r=snorp, a=lmandel
layout/tools/reftest/remotereftest.py
mobile/android/app/mobile.js
mobile/android/base/preferences/GeckoPreferences.java
mobile/android/base/updater/UpdateService.java
mobile/android/base/updater/UpdateServiceHelper.java
testing/profiles/prefs_general.js
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -341,16 +341,17 @@ class RemoteReftest(RefTest):
     def stopWebServer(self, options):
         self.server.stop()
 
     def createReftestProfile(self, options, reftestlist):
         profile = RefTest.createReftestProfile(self, options, reftestlist, server=options.remoteWebServer)
         profileDir = profile.profile
 
         prefs = {}
+        prefs["app.update.url.android"] = ""
         prefs["browser.firstrun.show.localepicker"] = False
         prefs["font.size.inflation.emPerLine"] = 0
         prefs["font.size.inflation.minTwips"] = 0
         prefs["reftest.remote"] = True
         # Set a future policy version to avoid the telemetry prompt.
         prefs["toolkit.telemetry.prompted"] = 999
         prefs["toolkit.telemetry.notifiedOptOut"] = 999
         prefs["reftest.uri"] = "%s" % reftestlist
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -498,24 +498,23 @@ pref("ui.windowframe", "#efebe7");
 
 /* prefs used by the update timer system (including blocklist pings) */
 pref("app.update.timerFirstInterval", 30000); // milliseconds
 pref("app.update.timerMinimumDelay", 30); // seconds
 
 // used by update service to decide whether or not to
 // automatically download an update
 pref("app.update.autodownload", "wifi");
+pref("app.update.url.android", "https://aus4.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%MOZ_VERSION%/update.xml");
 
 #ifdef MOZ_UPDATER
 /* prefs used specifically for updating the app */
 pref("app.update.enabled", false);
 pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
 
-// If you are looking for app.update.url, we no longer use it.
-// See mobile/android/base/updater/UpdateServiceHelper.java
 #endif
 
 // replace newlines with spaces on paste into single-line text boxes
 pref("editor.singleLine.pasteNewlines", 2);
 
 // threshold where a tap becomes a drag, in 1/240" reference pixels
 // The names of the preferences are to be in sync with EventStateManager.cpp
 pref("ui.dragThresholdX", 25);
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -111,16 +111,17 @@ OnSharedPreferenceChangeListener
     // These match keys in resources/xml*/preferences*.xml
     private static final String PREFS_SEARCH_RESTORE_DEFAULTS = NON_PREF_PREFIX + "search.restore_defaults";
     private static final String PREFS_DATA_REPORTING_PREFERENCES = NON_PREF_PREFIX + "datareporting.preferences";
     private static final String PREFS_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
     private static final String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
     private static final String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
     private static final String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
     private static final String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
+    private static final String PREFS_UPDATER_URL = "app.update.url.android";
     private static final String PREFS_GEO_REPORTING = NON_PREF_PREFIX + "app.geo.reportdata";
     private static final String PREFS_GEO_LEARN_MORE = NON_PREF_PREFIX + "geo.learn_more";
     private static final String PREFS_HEALTHREPORT_LINK = NON_PREF_PREFIX + "healthreport.link";
     private static final String PREFS_DEVTOOLS_REMOTE_ENABLED = "devtools.debugger.remote-enabled";
     private static final String PREFS_DISPLAY_REFLOW_ON_ZOOM = "browser.zoom.reflowOnZoom";
     private static final String PREFS_DISPLAY_TITLEBAR_MODE = "browser.chrome.titlebarMode";
     private static final String PREFS_SYNC = NON_PREF_PREFIX + "sync";
     private static final String PREFS_TRACKING_PROTECTION = "privacy.trackingprotection.enabled";
@@ -1054,16 +1055,18 @@ OnSharedPreferenceChangeListener
             // below, so we return here.
             return onLocaleSelected(Locales.getLanguageTag(lastLocale), (String) newValue);
         }
 
         if (PREFS_MENU_CHAR_ENCODING.equals(prefName)) {
             setCharEncodingState(((String) newValue).equals("true"));
         } else if (PREFS_UPDATER_AUTODOWNLOAD.equals(prefName)) {
             UpdateServiceHelper.setAutoDownloadPolicy(this, UpdateService.AutoDownloadPolicy.get((String) newValue));
+        } else if (PREFS_UPDATER_URL.equals(prefName)) {
+            UpdateServiceHelper.setUpdateUrl(this, (String) newValue);
         } else if (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(prefName)) {
             // The healthreport pref only lives in Android, so we do not persist
             // to Gecko, but we do broadcast intent to the health report
             // background uploader service, which will start or stop the
             // repeated background upload attempts.
             broadcastHealthReportUploadPref(this, (Boolean) newValue);
         } else if (PREFS_GEO_REPORTING.equals(prefName)) {
             broadcastStumblerPref(this, (Boolean) newValue);
--- a/mobile/android/base/updater/UpdateService.java
+++ b/mobile/android/base/updater/UpdateService.java
@@ -65,16 +65,17 @@ public class UpdateService extends Inten
 
     private static final String PREFS_NAME = "UpdateService";
     private static final String KEY_LAST_BUILDID = "UpdateService.lastBuildID";
     private static final String KEY_LAST_HASH_FUNCTION = "UpdateService.lastHashFunction";
     private static final String KEY_LAST_HASH_VALUE = "UpdateService.lastHashValue";
     private static final String KEY_LAST_FILE_NAME = "UpdateService.lastFileName";
     private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
     private static final String KEY_AUTODOWNLOAD_POLICY = "UpdateService.autoDownloadPolicy";
+    private static final String KEY_UPDATE_URL = "UpdateService.updateUrl";
 
     private SharedPreferences mPrefs;
 
     private NotificationManager mNotificationManager;
     private ConnectivityManager mConnectivityManager;
     private Builder mBuilder;
 
     private boolean mDownloading;
@@ -177,16 +178,21 @@ public class UpdateService extends Inten
             AutoDownloadPolicy policy = AutoDownloadPolicy.get(
                 intent.getIntExtra(UpdateServiceHelper.EXTRA_AUTODOWNLOAD_NAME,
                                    AutoDownloadPolicy.NONE.value));
 
             if (policy != AutoDownloadPolicy.NONE) {
                 setAutoDownloadPolicy(policy);
             }
 
+            String url = intent.getStringExtra(UpdateServiceHelper.EXTRA_UPDATE_URL_NAME);
+            if (url != null) {
+                setUpdateUrl(url);
+            }
+
             registerForUpdates(false);
         } else if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) {
             startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0));
             // Use this instead for forcing a download from about:fennec
             // startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD | UpdateServiceHelper.FLAG_REINSTALL);
         } else if (UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE.equals(intent.getAction())) {
             // We always want to do the download and apply it here
             mApplyImmediately = true;
@@ -349,17 +355,22 @@ public class UpdateService extends Inten
             }
         }
 
         return url.openConnection(proxy);
     }
 
     private UpdateInfo findUpdate(boolean force) {
         try {
-            URL url = UpdateServiceHelper.getUpdateUrl(this, force);
+            URL url = getUpdateUrl(force);
+
+            if (url == null) {
+              Log.e(LOGTAG, "failed to get update URL");
+              return null;
+            }
 
             DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
             Document dom = builder.parse(openConnectionWithProxy(url).getInputStream());
 
             NodeList nodes = dom.getElementsByTagName("update");
             if (nodes == null || nodes.getLength() == 0)
                 return null;
 
@@ -673,16 +684,26 @@ public class UpdateService extends Inten
     }
 
     private void setAutoDownloadPolicy(AutoDownloadPolicy policy) {
         SharedPreferences.Editor editor = mPrefs.edit();
         editor.putInt(KEY_AUTODOWNLOAD_POLICY, policy.value);
         editor.commit();
     }
 
+    private URL getUpdateUrl(boolean force) {
+        return UpdateServiceHelper.expandUpdateUrl(this, mPrefs.getString(KEY_UPDATE_URL, null), force);
+    }
+
+    private void setUpdateUrl(String url) {
+        SharedPreferences.Editor editor = mPrefs.edit();
+        editor.putString(KEY_UPDATE_URL, url);
+        editor.commit();
+    }
+
     private void saveUpdateInfo(UpdateInfo info, File downloaded) {
         SharedPreferences.Editor editor = mPrefs.edit();
         editor.putString(KEY_LAST_BUILDID, info.buildID);
         editor.putString(KEY_LAST_HASH_FUNCTION, info.hashFunction);
         editor.putString(KEY_LAST_HASH_VALUE, info.hashValue);
         editor.putString(KEY_LAST_FILE_NAME, downloaded.toString());
         editor.commit();
     }
--- a/mobile/android/base/updater/UpdateServiceHelper.java
+++ b/mobile/android/base/updater/UpdateServiceHelper.java
@@ -40,26 +40,28 @@ public class UpdateServiceHelper {
     protected static final String EXTRA_AUTODOWNLOAD_NAME = "autodownload";
 
     // Name of the Intent extra that holds the flags for ACTION_CHECK_FOR_UPDATE
     protected static final String EXTRA_UPDATE_FLAGS_NAME = "updateFlags";
 
     // Name of the Intent extra that holds the APK path, used with ACTION_APPLY_UPDATE
     protected static final String EXTRA_PACKAGE_PATH_NAME = "packagePath";
 
+    // Name of the Intent extra for the update URL, used with ACTION_REGISTER_FOR_UPDATES
+    protected static final String EXTRA_UPDATE_URL_NAME = "updateUrl";
+
     private static final String LOGTAG = "UpdateServiceHelper";
     private static final String DEFAULT_UPDATE_LOCALE = "en-US";
 
-    private static final String UPDATE_URL;
-
     // So that updates can be disabled by tests.
     private static volatile boolean isEnabled = true;
 
     private enum Pref {
-        AUTO_DOWNLOAD_POLICY("app.update.autodownload");
+        AUTO_DOWNLOAD_POLICY("app.update.autodownload"),
+        UPDATE_URL("app.update.url.android");
 
         public final String name;
 
         private Pref(String name) {
             this.name = name;
         }
 
         public final static String[] names;
@@ -75,74 +77,75 @@ public class UpdateServiceHelper {
             for (Pref id: Pref.values()) {
                 nameList.add(id.toString());
             }
 
             names = nameList.toArray(new String[0]);
         }
     }
 
-    static {
-        final String pkgSpecial;
-        if (AppConstants.MOZ_PKG_SPECIAL != null) {
-            pkgSpecial = "-" + AppConstants.MOZ_PKG_SPECIAL;
-        } else {
-            pkgSpecial = "";
-        }
-        UPDATE_URL = "https://aus4.mozilla.org/update/4/" + AppConstants.MOZ_APP_BASENAME + "/" +
-                     AppConstants.MOZ_APP_VERSION         +
-                     "/%BUILDID%/Android_"                + AppConstants.MOZ_APP_ABI + pkgSpecial +
-                     "/%LOCALE%/"                         + AppConstants.MOZ_UPDATE_CHANNEL +
-                     "/%OS_VERSION%/default/default/"     + AppConstants.MOZILLA_VERSION +
-                     "/update.xml";
-    }
-
     @RobocopTarget
     public static void setEnabled(final boolean enabled) {
         isEnabled = enabled;
     }
 
-    public static URL getUpdateUrl(Context context, boolean force) {
+    public static URL expandUpdateUrl(Context context, String updateUrl, boolean force) {
+        if (updateUrl == null) {
+            return null;
+        }
+
         PackageManager pm = context.getPackageManager();
 
-        String locale = null;
+        String pkgSpecial = AppConstants.MOZ_PKG_SPECIAL != null ?
+                            "-" + AppConstants.MOZ_PKG_SPECIAL :
+                            "";
+        String locale = DEFAULT_UPDATE_LOCALE;
+
         try {
             ApplicationInfo info = pm.getApplicationInfo(AppConstants.ANDROID_PACKAGE_NAME, 0);
             String updateLocaleUrl = "jar:jar:file://" + info.sourceDir + "!/" + AppConstants.OMNIJAR_NAME + "!/update.locale";
 
-            locale = GeckoJarReader.getText(updateLocaleUrl);
-
-            if (locale != null)
-                locale = locale.trim();
-            else
-                locale = DEFAULT_UPDATE_LOCALE;
+            final String jarLocale = GeckoJarReader.getText(updateLocaleUrl);
+            if (jarLocale != null) {
+                locale = jarLocale.trim();
+            }
         } catch (android.content.pm.PackageManager.NameNotFoundException e) {
             // Shouldn't really be possible, but fallback to default locale
-            Log.i(LOGTAG, "Failed to read update locale file, falling back to " + DEFAULT_UPDATE_LOCALE);
-            locale = DEFAULT_UPDATE_LOCALE;
+            Log.i(LOGTAG, "Failed to read update locale file, falling back to " + locale);
         }
 
-        String url = UPDATE_URL.replace("%LOCALE%", locale).
-            replace("%OS_VERSION%", Build.VERSION.RELEASE).
-            replace("%BUILDID%", force ? "0" : AppConstants.MOZ_APP_BUILDID);
+        String url = updateUrl.replace("%PRODUCT%", AppConstants.MOZ_APP_BASENAME)
+            .replace("%VERSION%", AppConstants.MOZ_APP_VERSION)
+            .replace("%BUILD_ID%", force ? "0" : AppConstants.MOZ_APP_BUILDID)
+            .replace("%BUILD_TARGET%", "Android_" + AppConstants.MOZ_APP_ABI + pkgSpecial)
+            .replace("%LOCALE%", locale)
+            .replace("%CHANNEL%", AppConstants.MOZ_UPDATE_CHANNEL)
+            .replace("%OS_VERSION%", Build.VERSION.RELEASE)
+            .replace("%DISTRIBUTION%", "default")
+            .replace("%DISTRIBUTION_VERSION%", "default")
+            .replace("%MOZ_VERSION%", AppConstants.MOZILLA_VERSION);
 
         try {
             return new URL(url);
         } catch (java.net.MalformedURLException e) {
             Log.e(LOGTAG, "Failed to create update url: ", e);
             return null;
         }
     }
 
     public static boolean isUpdaterEnabled() {
         return AppConstants.MOZ_UPDATER && isEnabled;
     }
 
+    public static void setUpdateUrl(Context context, String url) {
+        registerForUpdates(context, null, url);
+    }
+
     public static void setAutoDownloadPolicy(Context context, UpdateService.AutoDownloadPolicy policy) {
-        registerForUpdates(context, policy);
+        registerForUpdates(context, policy, null);
     }
 
     public static void checkForUpdate(Context context) {
         if (context == null) {
             return;
         }
 
         context.startService(createIntent(context, ACTION_CHECK_FOR_UPDATE));
@@ -170,29 +173,36 @@ public class UpdateServiceHelper {
         PrefsHelper.getPrefs(Pref.names, new PrefsHelper.PrefHandlerBase() {
             @Override public void prefValue(String pref, String value) {
                 prefs.put(pref, value);
             }
 
             @Override public void finish() {
                 UpdateServiceHelper.registerForUpdates(context,
                     UpdateService.AutoDownloadPolicy.get(
-                      (String) prefs.get(Pref.AUTO_DOWNLOAD_POLICY.toString())));
+                        (String) prefs.get(Pref.AUTO_DOWNLOAD_POLICY.toString())),
+                      (String) prefs.get(Pref.UPDATE_URL.toString()));
             }
         });
     }
 
-    public static void registerForUpdates(Context context, UpdateService.AutoDownloadPolicy policy) {
-        if (!isUpdaterEnabled())
-            return;
-
-        Log.i(LOGTAG, "register for updates policy: " + policy.toString());
+    public static void registerForUpdates(Context context, UpdateService.AutoDownloadPolicy policy, String url) {
+        if (!isUpdaterEnabled()) {
+             return;
+        }
 
         Intent intent = createIntent(context, ACTION_REGISTER_FOR_UPDATES);
-        intent.putExtra(EXTRA_AUTODOWNLOAD_NAME, policy.value);
+
+        if (policy != null) {
+            intent.putExtra(EXTRA_AUTODOWNLOAD_NAME, policy.value);
+        }
+
+        if (url != null) {
+            intent.putExtra(EXTRA_UPDATE_URL_NAME, url);
+        }
 
         context.startService(intent);
     }
 
     private static Intent createIntent(Context context, String action) {
         return new Intent(action, null, context, UpdateService.class);
     }
 }
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -36,16 +36,17 @@ user_pref("dom.min_background_timeout_va
 user_pref("test.mousescroll", true);
 user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs
 user_pref("network.http.prompt-temp-redirect", false);
 user_pref("media.cache_size", 1000);
 user_pref("media.volume_scale", "0.01");
 user_pref("security.warn_viewing_mixed", false);
 user_pref("app.update.enabled", false);
 user_pref("app.update.staging.enabled", false);
+user_pref("app.update.url.android", "");
 // Make sure GMPInstallManager won't hit the network.
 user_pref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
 user_pref("browser.panorama.experienced_first_run", true); // Assume experienced
 user_pref("dom.w3c_touch_events.enabled", 1);
 user_pref("dom.undo_manager.enabled", true);
 user_pref("dom.webcomponents.enabled", true);
 user_pref("dom.animations-api.core.enabled", true);
 // Set a future policy version to avoid the telemetry prompt.