Browse Source

Enable update notification (#1454)

* Enable update notification

* version check rather than build time

* fixup with Disable-Omaha-update-checks.patch

* taking the last-modified together with the redirect.

the check between build times in any case is currently commented (see isNewVersionAvailableByBuildTime)

* removed code related to build time check

* removed PREF_LATEST_MODIFIED_BUILDTIME
uazo 3 years ago
parent
commit
ad34a777bb
2 changed files with 938 additions and 2 deletions
  1. 2 2
      build/bromite_patches_list.txt
  2. 936 0
      build/patches/Enable-update-notification.patch

+ 2 - 2
build/bromite_patches_list.txt

@@ -34,7 +34,6 @@ Skip-the-first-run-and-metrics.patch
 Disable-all-promo-dialogs.patch
 Disable-all-promo-dialogs.patch
 Remove-signin-and-data-saver-integrations.patch
 Remove-signin-and-data-saver-integrations.patch
 Hide-passwords-manager-link.patch
 Hide-passwords-manager-link.patch
-Disable-Omaha-update-checks.patch
 Disable-update-scheduler.patch
 Disable-update-scheduler.patch
 Add-English-only-search-engine.patch
 Add-English-only-search-engine.patch
 Add-DuckDuckGo-Lite-search-engine.patch
 Add-DuckDuckGo-Lite-search-engine.patch
@@ -164,4 +163,5 @@ Enable-share-intent.patch
 Automated-domain-substitution.patch
 Automated-domain-substitution.patch
 Observe-WebContents-in-PPAPIDownloadRequest.patch
 Observe-WebContents-in-PPAPIDownloadRequest.patch
 Prevents-non-browser-processes-from-requesting-memory-dumps.patch
 Prevents-non-browser-processes-from-requesting-memory-dumps.patch
-Add-images-contentsettings.patch
+Add-images-contentsettings.patch
+Enable-update-notification.patch

+ 936 - 0
build/patches/Enable-update-notification.patch

@@ -0,0 +1,936 @@
+From: uazo <uazo@users.noreply.github.com>
+Date: Thu, 7 Oct 2021 14:27:12 +0000
+Subject: Enable update notification
+
+Enable checking for new version, with proxy support
+---
+ .../java/templates/BuildConfig.template       |   2 +
+ build/config/android/rules.gni                |   3 +
+ chrome/android/chrome_java_sources.gni        |   1 +
+ .../java/res/xml/about_chrome_preferences.xml |   5 +
+ .../about_settings/AboutChromeSettings.java   |  27 ++-
+ .../chrome/browser/omaha/OmahaBase.java       |  44 +++-
+ .../chrome/browser/omaha/UpdateConfigs.java   |   6 +-
+ .../browser/omaha/UpdateStatusProvider.java   |  36 ++--
+ .../browser/omaha/VersionNumberGetter.java    |   3 +-
+ .../inline/BromiteInlineUpdateController.java | 204 ++++++++++++++++++
+ .../inline/InlineUpdateControllerFactory.java |   8 +-
+ chrome/browser/endpoint_fetcher/BUILD.gn      |   2 +
+ .../endpoint_fetcher/endpoint_fetcher.cc      | 125 +++++++++++
+ .../endpoint_fetcher/endpoint_fetcher.h       |  23 +-
+ .../endpoint_fetcher/EndpointFetcher.java     |  11 +
+ .../EndpointHeaderResponse.java               |  31 +++
+ chrome/browser/flag-metadata.json             |   2 +-
+ chrome/browser/flag_descriptions.cc           |   5 +-
+ .../flags/android/chrome_feature_list.cc      |   2 +-
+ .../strings/android_chrome_strings.grd        |   8 +-
+ 20 files changed, 511 insertions(+), 37 deletions(-)
+ create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java
+ create mode 100644 chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java
+
+diff --git a/build/android/java/templates/BuildConfig.template b/build/android/java/templates/BuildConfig.template
+--- a/build/android/java/templates/BuildConfig.template
++++ b/build/android/java/templates/BuildConfig.template
+@@ -92,4 +92,6 @@ public class BuildConfig {
+ #else
+     public static MAYBE_FINAL boolean ISOLATED_SPLITS_ENABLED MAYBE_FALSE;
+ #endif
++
++    public static MAYBE_FINAL String BUILD_TARGET_CPU = _BUILD_TARGET_CPU;
+ }
+diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
+--- a/build/config/android/rules.gni
++++ b/build/config/android/rules.gni
+@@ -1967,6 +1967,9 @@ if (enable_java_templates) {
+           ]
+         }
+       }
++
++      # add arch to org.chromium.build.BuildConfig
++      defines += [ "_BUILD_TARGET_CPU=\"${target_cpu}\"" ]
+     }
+   }
+ 
+diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
+--- a/chrome/android/chrome_java_sources.gni
++++ b/chrome/android/chrome_java_sources.gni
+@@ -880,6 +880,7 @@ chrome_java_sources = [
+   "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java",
+   "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java",
+   "java/src/org/chromium/chrome/browser/omaha/inline/NoopInlineUpdateController.java",
++  "java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java",
+   "java/src/org/chromium/chrome/browser/omaha/inline/PlayInlineUpdateController.java",
+   "java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java",
+   "java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java",
+diff --git a/chrome/android/java/res/xml/about_chrome_preferences.xml b/chrome/android/java/res/xml/about_chrome_preferences.xml
+--- a/chrome/android/java/res/xml/about_chrome_preferences.xml
++++ b/chrome/android/java/res/xml/about_chrome_preferences.xml
+@@ -7,6 +7,11 @@
+     <Preference
+         android:key="application_version"
+         android:title="@string/application_version_title" />
++    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
++        android:key="allow_inline_update"
++        android:title="@string/allow_inline_update_title"
++        android:summary="@string/allow_inline_update_summary"
++        android:defaultValue="false" />
+     <org.chromium.chrome.browser.about_settings.AboutChromePreferenceOSVersion
+         android:key="os_version"
+         android:title="@string/os_version_title" />
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
+@@ -21,11 +21,16 @@ import org.chromium.ui.widget.Toast;
+ 
+ import java.util.Calendar;
+ 
++import android.content.SharedPreferences;
++import org.chromium.chrome.browser.omaha.OmahaBase;
++import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
++
+ /**
+  * Settings fragment that displays information about Chrome.
+  */
+ public class AboutChromeSettings
+-        extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
++        extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener,
++                                                    Preference.OnPreferenceChangeListener {
+     private static final int TAPS_FOR_DEVELOPER_SETTINGS = 7;
+ 
+     private static final String PREF_APPLICATION_VERSION = "application_version";
+@@ -59,6 +64,13 @@ public class AboutChromeSettings
+         p = findPreference(PREF_LEGAL_INFORMATION);
+         int currentYear = Calendar.getInstance().get(Calendar.YEAR);
+         p.setSummary(getString(R.string.legal_information_summary, currentYear));
++
++        ChromeSwitchPreference allowInlineUpdate =
++                (ChromeSwitchPreference) findPreference(OmahaBase.PREF_ALLOW_INLINE_UPDATE);
++        allowInlineUpdate.setChecked(
++            OmahaBase.getSharedPreferences()
++                    .getBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, false));
++        allowInlineUpdate.setOnPreferenceChangeListener(this);
+     }
+ 
+     /**
+@@ -122,4 +134,17 @@ public class AboutChromeSettings
+         }
+         return true;
+     }
++
++    @Override
++    public boolean onPreferenceChange(Preference preference, Object newValue) {
++        String key = preference.getKey();
++        if (OmahaBase.PREF_ALLOW_INLINE_UPDATE.equals(key)) {
++            SharedPreferences.Editor sharedPreferenceEditor = OmahaBase.getSharedPreferences().edit();
++            sharedPreferenceEditor.putBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, (boolean) newValue);
++            sharedPreferenceEditor.apply();
++
++            OmahaBase.resetUpdatePrefs();
++        }
++        return true;
++    }
+ }
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+@@ -31,6 +31,8 @@ import java.net.HttpURLConnection;
+ import java.net.URL;
+ import java.util.Date;
+ 
++import org.chromium.build.BuildConfig;
++
+ /**
+  * Keeps tabs on the current state of Chrome, tracking if and when a request should be sent to the
+  * Omaha Server.
+@@ -97,7 +99,9 @@ public class OmahaBase {
+     static final String PREF_TIMESTAMP_FOR_NEW_REQUEST = "timestampForNewRequest";
+     static final String PREF_TIMESTAMP_FOR_NEXT_POST_ATTEMPT = "timestampForNextPostAttempt";
+     static final String PREF_TIMESTAMP_OF_INSTALL = "timestampOfInstall";
+-    static final String PREF_TIMESTAMP_OF_REQUEST = "timestampOfRequest";
++    public static final String PREF_TIMESTAMP_OF_REQUEST = "timestampOfRequest";
++    public static final String PREF_LATEST_MODIFIED_VERSION = "latestModifiedVersion";
++    public static final String PREF_ALLOW_INLINE_UPDATE = "allow_inline_update";
+ 
+     static final int MIN_API_JOB_SCHEDULER = Build.VERSION_CODES.M;
+ 
+@@ -157,8 +161,9 @@ public class OmahaBase {
+     }
+ 
+     /** See {@link #sIsDisabled}. */
++    // do not enable version control with Omaha Update Server
+     static boolean isDisabled() {
+-        return sIsDisabled;
++        return true;
+     }
+ 
+     /**
+@@ -630,4 +635,39 @@ public class OmahaBase {
+                 // updateStatus is only used for the on-demand check.
+                 null);
+     }
++
++    public static boolean isNewVersionAvailableByVersion(String latestVersionString) {
++        VersionNumber mCurrentProductVersion = VersionNumber.fromString(ChromeVersionInfo.getProductVersion());
++        if (mCurrentProductVersion == null || latestVersionString == null)
++            return false;
++
++        VersionNumber latestVersion = VersionNumber.fromString(latestVersionString);
++        if (latestVersion == null) return false;
++
++        Log.i(TAG, "isNewVersionAvailable: CurrentProductVersion: %s",
++                mCurrentProductVersion.toString());
++        Log.i(TAG, "isNewVersionAvailable: LatestVersion: %s",
++                latestVersion.toString());
++
++        return mCurrentProductVersion.isSmallerThan(latestVersion);
++    }
++
++    public static void updateLastPushedTimeStamp(long timeMillis) {
++        SharedPreferences preferences = OmahaBase.getSharedPreferences();
++        SharedPreferences.Editor editor = preferences.edit();
++        editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, timeMillis);
++        editor.apply();
++    }
++
++    public static void setLastModifiedVersion(String version) {
++        SharedPreferences preferences = OmahaBase.getSharedPreferences();
++        SharedPreferences.Editor editor = preferences.edit();
++        editor.putString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, version);
++        editor.apply();
++    }
++
++    public static void resetUpdatePrefs() {
++        OmahaBase.setLastModifiedVersion("");
++        OmahaBase.updateLastPushedTimeStamp(0);
++    }
+ }
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java
+@@ -65,7 +65,7 @@ public class UpdateConfigs {
+     private static final String UPDATE_NOTIFICATION_EXPERIMENTAL_PARAM_NAME =
+             "update_notification_experimental_context";
+ 
+-    private static final long DEFAULT_UPDATE_NOTIFICATION_INTERVAL = 21 * DateUtils.DAY_IN_MILLIS;
++    private static final long DEFAULT_UPDATE_NOTIFICATION_INTERVAL = 7 * DateUtils.DAY_IN_MILLIS;
+     private static final long DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS = 2 * DateUtils.DAY_IN_MILLIS;
+ 
+     /** Possible update flow configurations. */
+@@ -300,7 +300,7 @@ public class UpdateConfigs {
+      * @return the current inline update flow configuration.
+      */
+     @UpdateFlowConfiguration
+-    static int getConfiguration() {
++    public static int getConfiguration() {
+         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.INLINE_UPDATE_FLOW)) {
+             // Always use the the old flow if the inline update flow feature is not enabled.
+             return UpdateFlowConfiguration.INTENT_ONLY;
+@@ -328,4 +328,4 @@ public class UpdateConfigs {
+         if (configuration == null) return "";
+         return configuration.toLowerCase(Locale.US);
+     }
+-}
+\ No newline at end of file
++}
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java
+@@ -45,6 +45,12 @@ import java.io.File;
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.RetentionPolicy;
+ 
++import org.chromium.base.Log;
++import android.content.SharedPreferences;
++import android.os.Build;
++import org.chromium.build.BuildConfig;
++import org.chromium.chrome.browser.version.ChromeVersionInfo;
++
+ /**
+  * Provides the current update state for Chrome.  This update state is asynchronously determined and
+  * can change as Chrome runs.
+@@ -112,6 +118,8 @@ public class UpdateStatusProvider implements ActivityStateListener {
+          */
+         public String latestUnsupportedVersion;
+ 
++        public long latestLastModifiedBuildTime;
++
+         /**
+          * Whether or not we are currently trying to simulate the update.  Used to ignore other
+          * update signals.
+@@ -133,6 +141,7 @@ public class UpdateStatusProvider implements ActivityStateListener {
+             latestUnsupportedVersion = other.latestUnsupportedVersion;
+             mIsSimulated = other.mIsSimulated;
+             mIsInlineSimulated = other.mIsInlineSimulated;
++            latestLastModifiedBuildTime = other.latestLastModifiedBuildTime;
+         }
+     }
+ 
+@@ -339,8 +348,7 @@ public class UpdateStatusProvider implements ActivityStateListener {
+             case UpdateConfigs.UpdateFlowConfiguration.NEVER_SHOW:
+                 return UpdateState.NONE;
+             case UpdateConfigs.UpdateFlowConfiguration.INLINE_ONLY:
+-                if (omahaState != UpdateState.UPDATE_AVAILABLE) return omahaState;
+-                if (inlineState == UpdateState.NONE) return UpdateState.NONE;
++                if (inlineState == UpdateState.NONE) return omahaState;
+                 return inlineState;
+             case UpdateConfigs.UpdateFlowConfiguration.BEST_EFFORT:
+                 if (omahaState != UpdateState.UPDATE_AVAILABLE) return omahaState;
+@@ -415,24 +423,12 @@ public class UpdateStatusProvider implements ActivityStateListener {
+         private UpdateStatus getRealStatus(Context context) {
+             UpdateStatus status = new UpdateStatus();
+ 
+-            if (VersionNumberGetter.isNewerVersionAvailable(context)) {
+-                status.updateUrl = MarketURLGetter.getMarketUrl();
+-                status.latestVersion =
+-                        VersionNumberGetter.getInstance().getLatestKnownVersion(context);
+-
+-                boolean allowedToUpdate =
+-                        checkForSufficientStorage() && isGooglePlayStoreAvailable(context);
+-                status.updateState =
+-                        allowedToUpdate ? UpdateState.UPDATE_AVAILABLE : UpdateState.NONE;
+-
+-                SharedPreferencesManager.getInstance().removeKey(
+-                        ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION);
+-            } else if (!VersionNumberGetter.isCurrentOsVersionSupported()) {
+-                status.updateState = UpdateState.UNSUPPORTED_OS_VERSION;
+-                status.latestUnsupportedVersion = SharedPreferencesManager.getInstance().readString(
+-                        ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION, null);
+-            } else {
+-                status.updateState = UpdateState.NONE;
++            SharedPreferences preferences = OmahaBase.getSharedPreferences();
++            status.latestVersion = preferences.getString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, "");
++
++            status.updateState = UpdateState.NONE;
++            if (OmahaBase.isNewVersionAvailableByVersion(status.latestVersion)) {
++                status.updateState = UpdateState.INLINE_UPDATE_AVAILABLE;
+             }
+ 
+             return status;
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java
+@@ -43,7 +43,8 @@ public class VersionNumberGetter {
+     private static VersionNumberGetter sInstanceForTests;
+ 
+     /** If false, OmahaClient will never report that a newer version is available. */
+-    private static boolean sEnableUpdateDetection = true;
++    // it must be false to not enable version control with Omaha server
++    private static boolean sEnableUpdateDetection = false;
+ 
+     protected VersionNumberGetter() { }
+ 
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java
+new file mode 100644
+--- /dev/null
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java
+@@ -0,0 +1,204 @@
++// Copyright 2021 The Ungoogled Chromium Authors. All rights reserved.
++//
++// This file is part of Ungoogled Chromium Android.
++//
++// Ungoogled Chromium Android is free software: you can redistribute it
++// and/or modify it under the terms of the GNU General Public License as
++// published by the Free Software Foundation, either version 3 of the
++// License, or any later version.
++//
++// Ungoogled Chromium Android is distributed in the hope that it will be
++// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
++// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++// GNU General Public License for more details.
++//
++// You should have received a copy of the GNU General Public License
++// along with Ungoogled Chromium Android.  If not,
++// see <https://www.gnu.org/licenses/>.
++
++package org.chromium.chrome.browser.omaha.inline;
++
++import static org.chromium.chrome.browser.omaha.UpdateConfigs.getUpdateNotificationInterval;
++
++import android.app.Activity;
++import android.content.SharedPreferences;
++import android.os.Build;
++import android.text.format.DateUtils;
++import org.chromium.build.BuildConfig;
++
++import androidx.annotation.Nullable;
++
++import org.chromium.base.Callback;
++import org.chromium.base.Log;
++import org.chromium.base.task.AsyncTask;
++import org.chromium.base.task.PostTask;
++import org.chromium.chrome.browser.app.ChromeActivity;
++import org.chromium.chrome.browser.omaha.OmahaBase;
++import org.chromium.chrome.browser.omaha.UpdateConfigs;
++import org.chromium.chrome.browser.omaha.UpdateStatusProvider;
++import org.chromium.chrome.browser.profiles.Profile;
++import org.chromium.chrome.browser.tab.TabLaunchType;
++import org.chromium.chrome.browser.tabmodel.TabCreator;
++import org.chromium.chrome.browser.version.ChromeVersionInfo;
++import org.chromium.content_public.browser.LoadUrlParams;
++import org.chromium.content_public.browser.UiThreadTaskTraits;
++import org.chromium.ui.base.PageTransition;
++
++import java.io.BufferedReader;
++import java.io.IOException;
++import java.io.InputStreamReader;
++import java.io.InputStream;
++import java.net.MalformedURLException;
++import java.net.URL;
++import java.net.HttpURLConnection;
++import java.util.regex.Pattern;
++
++import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
++import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
++import org.chromium.chrome.browser.omaha.VersionNumber;
++
++class BromiteInlineUpdateController implements InlineUpdateController {
++
++    private static final String TAG = "BromiteInlineUpdateController";
++    private final String UPDATE_VERSION_URL = "https://github.com/bromite/bromite/releases/latest/download/";
++
++    private String getDownloadUrl() {
++        return UPDATE_VERSION_URL + BuildConfig.BUILD_TARGET_CPU + "_ChromePublic.apk";
++    }
++
++    private boolean mEnabled = true;
++    private Runnable mCallback;
++    private @Nullable @UpdateStatusProvider.UpdateState Integer mUpdateState =
++                                                    UpdateStatusProvider.UpdateState.NONE;
++
++    BromiteInlineUpdateController(Runnable callback) {
++        mCallback = callback;
++    }
++
++    @Override
++    public void setEnabled(boolean enabled) {
++        if (mEnabled == enabled) return;
++
++        mEnabled = enabled;
++        if (mEnabled) pullCurrentState();
++    }
++
++    @Override
++    public @Nullable @UpdateStatusProvider.UpdateState Integer getStatus() {
++        if (mEnabled) pullCurrentState();
++        return mUpdateState;
++    }
++
++    @Override
++    public void startUpdate(Activity activity) {
++        assert ChromeActivity.class.isInstance(activity);
++        ChromeActivity thisActivity = (ChromeActivity) activity;
++        String downloadUrl = getDownloadUrl();
++        // Always open in new incognito tab
++        TabCreator tabCreator = thisActivity.getTabCreator(true);
++        tabCreator.createNewTab(new LoadUrlParams(downloadUrl, PageTransition.AUTO_BOOKMARK),
++                TabLaunchType.FROM_LINK, thisActivity.getActivityTab());
++    }
++
++    @Override
++    public void completeUpdate() {
++    }
++
++    private void pullCurrentState() {
++        if (OmahaBase.getSharedPreferences()
++                .getBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, false) == false) {
++            Log.i(TAG, "Checking for update disabled by user");
++            return;
++        }
++
++        if (mUpdateState != UpdateStatusProvider.UpdateState.NONE)
++            return;
++
++        if (shallUpdate() == false)
++            return;
++
++        switch (mUpdateState) {
++            case UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE:
++                break;
++            case UpdateStatusProvider.UpdateState.NONE:
++                checkLatestVersion((ok) -> {
++                    if (ok == false) return;
++
++                    SharedPreferences preferences = OmahaBase.getSharedPreferences();
++                    String latestVersion = preferences.getString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, "");
++
++                    if (OmahaBase.isNewVersionAvailableByVersion(latestVersion)) {
++                        postStatus(UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE);
++                    } else {
++                        if (mUpdateState != UpdateStatusProvider.UpdateState.NONE) {
++                            postStatus(UpdateStatusProvider.UpdateState.NONE);
++                        }
++                    }
++                });
++                break;
++            case UpdateStatusProvider.UpdateState.INLINE_UPDATE_READY:
++                // Intentional fall through.
++            case UpdateStatusProvider.UpdateState.INLINE_UPDATE_FAILED:
++                // Intentional fall through.
++            case UpdateStatusProvider.UpdateState.INLINE_UPDATE_DOWNLOADING:
++                // Intentional fall through.
++            case UpdateStatusProvider.UpdateState.UNSUPPORTED_OS_VERSION:
++                // Intentional fall through.
++            default:
++                return;
++        }
++    }
++
++    private boolean shallUpdate() {
++        long currentTime = System.currentTimeMillis();
++        SharedPreferences preferences = OmahaBase.getSharedPreferences();
++        long lastPushedTimeStamp = preferences.getLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, 0);
++        return currentTime - lastPushedTimeStamp >= getUpdateNotificationInterval();
++    }
++
++    private void checkLatestVersion(final Callback<Boolean> callback) {
++        assert UPDATE_VERSION_URL != null;
++
++        OmahaBase.resetUpdatePrefs();
++
++        String urlToCheck = getDownloadUrl();
++        Log.i(TAG, "Fetching with HEAD: " + urlToCheck);
++
++        EndpointFetcher.nativeHeadWithNoAuth(
++                (endpointResponse) -> {
++                    Log.i(TAG, "Obtained response");
++                    Log.i(TAG, "with message: '%s'", endpointResponse.getResponseString());
++                    Log.i(TAG, "and redirect: '%s'", endpointResponse.getRedirectUrl());
++
++                    OmahaBase.updateLastPushedTimeStamp(System.currentTimeMillis());
++
++                    if (endpointResponse.getRedirectUrl() != null) {
++                        String[] parts = endpointResponse.getRedirectUrl()
++                                                         .split(Pattern.quote("/"));
++                        for(String part:parts) {
++                            VersionNumber version = VersionNumber.fromString(part);
++                            if (version != null) {
++                                OmahaBase.setLastModifiedVersion(part);
++                                callback.onResult(true);
++                                return;
++                            }
++                        }
++                    } else {
++                        // retry after 1 hour
++                        OmahaBase.updateLastPushedTimeStamp(
++                            System.currentTimeMillis() - getUpdateNotificationInterval() -
++                            DateUtils.HOUR_IN_MILLIS * 1);
++                        Log.i(TAG, "Some error has occurred. I'll try again in 1 hour");
++                    }
++
++                    callback.onResult(false);
++                },
++                Profile.getLastUsedRegularProfile(),
++                urlToCheck, /*timeout*/5000, /*follow_redirect*/false);
++    }
++
++    private void postStatus(@UpdateStatusProvider.UpdateState int status) {
++        mUpdateState = status;
++        PostTask.postTask(UiThreadTaskTraits.DEFAULT, mCallback);
++    }
++}
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java
+@@ -19,8 +19,10 @@ public class InlineUpdateControllerFactory {
+         @FakeAppUpdateManagerWrapper.Type
+         int mockInlineEndState = UpdateConfigs.getMockInlineScenarioEndState();
+ 
+-        // No test scenario was in place, and the inline flow has not been enabled, so use a
+-        // controller with no functionality.
+-        return new NoopInlineUpdateController(callback);
++        if (ChromeFeatureList.isEnabled(ChromeFeatureList.INLINE_UPDATE_FLOW)) {
++            return new BromiteInlineUpdateController(callback);
++        } else {
++            return new NoopInlineUpdateController(callback);
++        }
+     }
+ }
+diff --git a/chrome/browser/endpoint_fetcher/BUILD.gn b/chrome/browser/endpoint_fetcher/BUILD.gn
+--- a/chrome/browser/endpoint_fetcher/BUILD.gn
++++ b/chrome/browser/endpoint_fetcher/BUILD.gn
+@@ -14,6 +14,7 @@ android_library("java") {
+   sources = [
+     "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java",
+     "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java",
++    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java",
+   ]
+   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+ }
+@@ -22,5 +23,6 @@ generate_jni("jni_headers") {
+   sources = [
+     "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java",
+     "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java",
++    "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java",
+   ]
+ }
+diff --git a/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc b/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
+--- a/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
++++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher.cc
+@@ -26,10 +26,15 @@
+ #include "base/android/jni_string.h"
+ #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointFetcher_jni.h"
+ #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointResponse_jni.h"
++#include "chrome/browser/endpoint_fetcher/jni_headers/EndpointHeaderResponse_jni.h"
+ #include "chrome/browser/profiles/profile_android.h"
+ 
+ #endif  // defined(OS_ANDROID)
+ 
++#include "net/base/load_flags.h"
++#include "net/http/http_status_code.h"
++#include "services/network/public/cpp/resource_request.h"
++
+ namespace {
+ const char kContentTypeKey[] = "Content-Type";
+ const char kDeveloperKey[] = "X-Developer-Key";
+@@ -150,6 +155,19 @@ EndpointFetcher::EndpointFetcher(
+       identity_manager_(nullptr),
+       sanitize_response_(true) {}
+ 
++EndpointFetcher::EndpointFetcher(
++    Profile* const profile,
++    const GURL& url,
++    int64_t timeout_ms,
++    const net::NetworkTrafficAnnotationTag& annotation_tag)
++    : url_(url),
++      timeout_ms_(timeout_ms),
++      annotation_tag_(annotation_tag),
++      url_loader_factory_(profile->GetDefaultStoragePartition()
++                              ->GetURLLoaderFactoryForBrowserProcess()),
++      identity_manager_(nullptr),
++      sanitize_response_(false) {}
++
+ EndpointFetcher::~EndpointFetcher() = default;
+ 
+ void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) {
+@@ -279,6 +297,73 @@ void EndpointFetcher::OnSanitizationResult(
+   std::move(endpoint_fetcher_callback).Run(std::move(response));
+ }
+ 
++void EndpointFetcher::PerformHeadRequest(
++    EndpointFetcherCallback endpoint_fetcher_callback,
++    const char* key,
++    bool allow_redirect) {
++
++  endpoint_fetcher_callback_ = std::move(endpoint_fetcher_callback);
++
++  auto resource_request = std::make_unique<network::ResourceRequest>();
++  resource_request->method = "HEAD";
++  resource_request->url = url_;
++  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
++  resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE
++                                  | net::LOAD_DO_NOT_SAVE_COOKIES;
++  if (allow_redirect == false) {
++    resource_request->redirect_mode = network::mojom::RedirectMode::kManual;
++  }
++
++  simple_url_loader_ = network::SimpleURLLoader::Create(
++      std::move(resource_request), annotation_tag_);
++  simple_url_loader_->SetTimeoutDuration(
++      base::TimeDelta::FromMilliseconds(timeout_ms_));
++  simple_url_loader_->SetAllowHttpErrorResults(true);
++
++  if(!response_)
++    response_ = std::make_unique<EndpointResponse>();
++
++  if (allow_redirect == false) {
++    simple_url_loader_->SetOnRedirectCallback(base::BindRepeating(
++      &EndpointFetcher::OnSimpleLoaderRedirect, base::Unretained(this)));
++  }
++
++  simple_url_loader_->DownloadHeadersOnly(
++      url_loader_factory_.get(),
++      base::BindOnce(&EndpointFetcher::OnURLLoadComplete,
++                     base::Unretained(this)));
++}
++
++void EndpointFetcher::OnSimpleLoaderRedirect(
++    const net::RedirectInfo& redirect_info,
++    const network::mojom::URLResponseHead& response_head,
++    std::vector<std::string>* removed_headers) {
++  url_ = redirect_info.new_url;
++  if (response_->redirect_url.empty())
++    response_->redirect_url = url_.spec();
++
++  PerformHeadRequest(std::move(endpoint_fetcher_callback_), nullptr, true);
++}
++
++void EndpointFetcher::OnURLLoadComplete(
++    scoped_refptr<net::HttpResponseHeaders> headers) {
++  if (!endpoint_fetcher_callback_)
++    return;
++
++  if (headers) {
++    if (response_->redirect_url.empty()) {
++      std::string location;
++      if (simple_url_loader_->ResponseInfo()->headers->IsRedirect(&location)) {
++        response_->redirect_url = location;
++      }
++    }
++  }
++
++  std::string net_error = net::ErrorToString(simple_url_loader_->NetError());
++  response_->response = net_error;
++  std::move(endpoint_fetcher_callback_).Run(std::move(response_));
++}
++
+ #if defined(OS_ANDROID)
+ namespace {
+ static void OnEndpointFetcherComplete(
+@@ -295,6 +380,25 @@ static void OnEndpointFetcherComplete(
+                        base::android::AttachCurrentThread(),
+                        std::move(endpoint_response->response))));
+ }
++
++static void OnEndpointFetcherHeadComplete(
++    const base::android::JavaRef<jobject>& jcaller,
++    // Passing the endpoint_fetcher ensures the endpoint_fetcher's
++    // lifetime extends to the callback and is not destroyed
++    // prematurely (which would result in cancellation of the request).
++    std::unique_ptr<EndpointFetcher> endpoint_fetcher,
++    std::unique_ptr<EndpointResponse> endpoint_response) {
++  base::android::RunObjectCallbackAndroid(
++      jcaller, Java_EndpointHeaderResponse_createEndpointResponse(
++                   base::android::AttachCurrentThread(),
++                   base::android::ConvertUTF8ToJavaString(
++                       base::android::AttachCurrentThread(),
++                       std::move(endpoint_response->response)),
++                   base::android::ConvertUTF8ToJavaString(
++                       base::android::AttachCurrentThread(),
++                       std::move(endpoint_response->redirect_url))));
++}
++
+ }  // namespace
+ 
+ // TODO(crbug.com/1077537) Create a KeyProvider so
+@@ -380,4 +484,25 @@ static void JNI_EndpointFetcher_NativeFetchWithNoAuth(
+       nullptr);
+ }
+ 
++static void JNI_EndpointFetcher_NativeHeadWithNoAuth(
++    JNIEnv* env,
++    const base::android::JavaParamRef<jobject>& jprofile,
++    const base::android::JavaParamRef<jstring>& jurl,
++    jlong jtimeout, jboolean allow_redirect,
++    const base::android::JavaParamRef<jobject>& jcallback) {
++  auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
++      ProfileAndroid::FromProfileAndroid(jprofile),
++      GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
++      jtimeout,
++      NO_TRAFFIC_ANNOTATION_YET);
++  auto* const endpoint_fetcher_ptr = endpoint_fetcher.get();
++  endpoint_fetcher_ptr->PerformHeadRequest(
++      base::BindOnce(&OnEndpointFetcherHeadComplete,
++                     base::android::ScopedJavaGlobalRef<jobject>(jcallback),
++                     // unique_ptr endpoint_fetcher is passed until the callback
++                     // to ensure its lifetime across the request.
++                     std::move(endpoint_fetcher)),
++      nullptr, allow_redirect);
++}
++
+ #endif  // defined(OS_ANDROID)
+diff --git a/chrome/browser/endpoint_fetcher/endpoint_fetcher.h b/chrome/browser/endpoint_fetcher/endpoint_fetcher.h
+--- a/chrome/browser/endpoint_fetcher/endpoint_fetcher.h
++++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher.h
+@@ -14,6 +14,8 @@
+ #include "components/signin/public/identity_manager/scope_set.h"
+ #include "net/traffic_annotation/network_traffic_annotation.h"
+ #include "services/data_decoder/public/cpp/json_sanitizer.h"
++#include "services/network/public/cpp/resource_request.h"
++#include "services/network/public/mojom/url_response_head.mojom.h"
+ 
+ namespace network {
+ struct ResourceRequest;
+@@ -31,6 +33,8 @@ class Profile;
+ 
+ struct EndpointResponse {
+   std::string response;
++  long last_modified;
++  std::string redirect_url;
+   // TODO(crbug.com/993393) Add more detailed error messaging
+ };
+ 
+@@ -77,6 +81,12 @@ class EndpointFetcher {
+                   const GURL& url,
+                   const net::NetworkTrafficAnnotationTag& annotation_tag);
+ 
++  // Constructor if no authentication is needed, with timeout
++  EndpointFetcher(Profile* const profile,
++                  const GURL& url,
++                  int64_t timeout_ms,
++                  const net::NetworkTrafficAnnotationTag& annotation_tag);
++
+   // Used for tests. Can be used if caller constructs their own
+   // url_loader_factory and identity_manager.
+   EndpointFetcher(
+@@ -113,6 +123,10 @@ class EndpointFetcher {
+   virtual void PerformRequest(EndpointFetcherCallback endpoint_fetcher_callback,
+                               const char* key);
+ 
++  virtual void PerformHeadRequest(EndpointFetcherCallback endpoint_fetcher_callback,
++                                  const char* key,
++                                  bool allow_redirect);
++
+  protected:
+   // Used for Mock only. see MockEndpointFetcher class.
+   explicit EndpointFetcher(
+@@ -126,6 +140,10 @@ class EndpointFetcher {
+                          std::unique_ptr<std::string> response_body);
+   void OnSanitizationResult(EndpointFetcherCallback endpoint_fetcher_callback,
+                             data_decoder::JsonSanitizer::Result result);
++  void OnURLLoadComplete(scoped_refptr<net::HttpResponseHeaders> headers);
++  void OnSimpleLoaderRedirect(const net::RedirectInfo& redirect_info,
++                              const network::mojom::URLResponseHead& response_head,
++                              std::vector<std::string>* removed_headers);
+ 
+   enum AuthType { CHROME_API_KEY, OAUTH, NO_AUTH };
+   AuthType auth_type_;
+@@ -133,7 +151,7 @@ class EndpointFetcher {
+   // Members set in constructor to be passed to network::ResourceRequest or
+   // network::SimpleURLLoader.
+   const std::string oauth_consumer_name_;
+-  const GURL url_;
++  GURL url_;
+   const std::string http_method_;
+   const std::string content_type_;
+   int64_t timeout_ms_;
+@@ -152,6 +170,9 @@ class EndpointFetcher {
+       access_token_fetcher_;
+   std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+ 
++  EndpointFetcherCallback endpoint_fetcher_callback_;
++  std::unique_ptr<EndpointResponse> response_;
++
+   base::WeakPtrFactory<EndpointFetcher> weak_ptr_factory_{this};
+ };
+ 
+diff --git a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
+--- a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
++++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java
+@@ -68,6 +68,14 @@ public final class EndpointFetcher {
+                 profile, url, httpsMethod, contentType, postData, timeout, headers, callback);
+     }
+ 
++    @MainThread
++    public static void nativeHeadWithNoAuth(
++            Callback<EndpointHeaderResponse> callback, Profile profile,
++            String url, long timeout, boolean allow_redirect) {
++        EndpointFetcherJni.get().nativeHeadWithNoAuth(
++                profile, url, timeout, allow_redirect, callback);
++    }
++
+     @NativeMethods
+     public interface Natives {
+         void nativeFetchOAuth(Profile profile, String oathConsumerName, String url,
+@@ -78,5 +86,8 @@ public final class EndpointFetcher {
+                 Callback<EndpointResponse> callback);
+         void nativeFetchWithNoAuth(
+                 Profile profile, String url, Callback<EndpointResponse> callback);
++        void nativeHeadWithNoAuth(
++                Profile profile, String url, long timeout, boolean allow_redirect,
++                Callback<EndpointHeaderResponse> callback);
+     }
+ }
+diff --git a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java
+new file mode 100644
+--- /dev/null
++++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java
+@@ -0,0 +1,31 @@
++// Copyright 2019 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++package org.chromium.chrome.browser.endpoint_fetcher;
++
++import org.chromium.base.annotations.CalledByNative;
++
++public class EndpointHeaderResponse {
++    private final String mResponseString;
++    private final String mRedirectUrl;
++
++    public EndpointHeaderResponse(String responseString, String redirectUrl) {
++        mResponseString = responseString;
++        mRedirectUrl = redirectUrl;
++    }
++
++    public String getResponseString() {
++        return mResponseString;
++    }
++
++    public String getRedirectUrl() {
++        return mRedirectUrl;
++    }
++
++    @CalledByNative
++    private static EndpointHeaderResponse createEndpointResponse(
++            String response, String redirectUrl) {
++        return new EndpointHeaderResponse(response, redirectUrl);
++    }
++}
+diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
+--- a/chrome/browser/flag-metadata.json
++++ b/chrome/browser/flag-metadata.json
+@@ -2059,7 +2059,7 @@
+   {
+     "name": "enable-inline-update-flow",
+     "owners": [ "nyquist", "dtrainor" ],
+-    "expiry_milestone": 83
++    "expiry_milestone": -1
+   },
+   {
+     "name": "enable-input-event-logging",
+diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
+--- a/chrome/browser/flag_descriptions.cc
++++ b/chrome/browser/flag_descriptions.cc
+@@ -3705,10 +3705,9 @@ const char kVoiceButtonInTopToolbarDescription[] =
+     "Enables showing the voice search button in the top toolbar. Enabling "
+     "Adaptive Button overrides this.";
+ 
+-const char kInlineUpdateFlowName[] = "Enable Google Play inline update flow";
++const char kInlineUpdateFlowName[] = "Enable inline update flow";
+ const char kInlineUpdateFlowDescription[] =
+-    "When this flag is set, instead of taking the user to the Google Play "
+-    "Store when an update is available, the user is presented with an inline "
++    "When this flag is set, the user is presented with an inline "
+     "flow where they do not have to leave Chrome until the update is ready "
+     "to install.";
+ 
+diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
+--- a/chrome/browser/flags/android/chrome_feature_list.cc
++++ b/chrome/browser/flags/android/chrome_feature_list.cc
+@@ -594,7 +594,7 @@ const base::Feature kIncognitoScreenshot{"IncognitoScreenshot",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+ 
+ const base::Feature kInlineUpdateFlow{"InlineUpdateFlow",
+-                                      base::FEATURE_DISABLED_BY_DEFAULT};
++                                      base::FEATURE_ENABLED_BY_DEFAULT};
+ 
+ const base::Feature kInstantStart{"InstantStart",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
+--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
+@@ -1631,6 +1631,12 @@ Your Google account may have other forms of browsing history like searches and a
+       <message name="IDS_DEPRECATION_WARNING" desc="Warning about Chrome updates no longer being supported">
+         Chrome updates are no longer supported for this version of Android
+       </message>
++      <message name="IDS_ALLOW_INLINE_UPDATE_TITLE" desc="Title for allow inline update preference">
++        Allow checking for updates
++      </message>
++      <message name="IDS_ALLOW_INLINE_UPDATE_SUMMARY" desc="Summary for allow inline update preference">
++        Check for updates by contacting the Bromite repo
++      </message>
+ 
+       <!-- Account management UI strings. -->
+       <message name="IDS_ACCOUNT_MANAGEMENT_TITLE" desc="Header title for the account management screen. [CHAR_LIMIT=32]">
+@@ -3059,7 +3065,7 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
+ 
+       <!-- Main menu items -->
+       <message name="IDS_MENU_UPDATE" desc="Menu item for updating chrome. [CHAR_LIMIT=24]">
+-        Update Chrome
++        Update Bromite
+       </message>
+       <message name="IDS_MENU_UPDATE_SUMMARY_DEFAULT" desc="Summary string for update menu item explaining that a newer version of Chrome is available. [CHAR_LIMIT=30]">
+         Newer version is available
+-- 
+2.17.1
+