From: uazo Date: Thu, 7 Oct 2021 14:27:12 +0000 Subject: Bromite auto updater Enable checking for new versions, with notifications and proxy support. Restore InlineUpdateFlow feature. Some parts authored by csagan5. License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html --- .../java/templates/BuildConfig.template | 2 + build/config/android/rules.gni | 3 + chrome/android/chrome_java_sources.gni | 2 + .../java/res/xml/about_chrome_preferences.xml | 5 + .../about_settings/AboutChromeSettings.java | 28 +- .../omaha/CromiteUpdateStatusProvider.java | 42 +++ .../chrome/browser/omaha/OmahaBase.java | 7 +- .../browser/omaha/UpdateMenuItemHelper.java | 89 +++++- .../inline/BromiteInlineUpdateController.java | 285 ++++++++++++++++++ chrome/browser/BUILD.gn | 10 + chrome/browser/endpoint_fetcher/BUILD.gn | 32 ++ chrome/browser/endpoint_fetcher/DEPS | 3 + chrome/browser/endpoint_fetcher/OWNERS | 1 + .../endpoint_fetcher_android.cc | 109 +++++++ .../endpoint_fetcher/EndpointFetcher.java | 63 ++++ .../EndpointHeaderResponse.java | 31 ++ .../endpoint_fetcher/EndpointResponse.java | 30 ++ .../flags/android/chrome_feature_list.cc | 1 + .../browser/flags/ChromeFeatureList.java | 1 + .../access_code_cast_discovery_interface.cc | 2 +- chrome/browser/omaha/android/BUILD.gn | 3 + .../chrome/browser/omaha/OmahaPrefUtils.java | 52 +++- .../chrome/browser/omaha/UpdateConfigs.java | 27 +- .../browser/omaha/UpdateStatusProvider.java | 168 +++++++---- .../browser/omaha/VersionNumberGetter.java | 4 +- .../omaha/inline/InlineUpdateController.java | 57 ++++ .../safety_hub/SafetyHubFetchService.java | 5 - .../browser/save_to_drive/drive_uploader.cc | 2 +- .../strings/android_chrome_strings.grd | 23 +- .../ui/lens/lens_overlay_query_controller.cc | 2 +- components/commerce/core/account_checker.cc | 2 +- .../subscriptions_server_proxy.cc | 2 +- .../internal/composebox_query_controller.cc | 2 +- .../data_sharing_network_loader_impl.cc | 2 +- .../internal/preview_server_proxy.cc | 2 +- .../endpoint_fetcher/endpoint_fetcher.cc | 127 ++++++-- .../endpoint_fetcher/endpoint_fetcher.h | 28 +- components/manta/base_provider.cc | 4 +- .../Bromite-auto-updater.inc | 3 + .../Bromite-auto-updater.inc | 1 + 40 files changed, 1158 insertions(+), 104 deletions(-) create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/CromiteUpdateStatusProvider.java create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java create mode 100644 chrome/browser/endpoint_fetcher/BUILD.gn create mode 100644 chrome/browser/endpoint_fetcher/DEPS create mode 100644 chrome/browser/endpoint_fetcher/OWNERS create mode 100644 chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc create mode 100644 chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java create mode 100644 chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java create mode 100644 chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java create mode 100644 chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java create mode 100644 cromite_flags/chrome/browser/flags/android/chrome_feature_list_cc/Bromite-auto-updater.inc create mode 100644 cromite_flags/chrome/browser/flags/android/chrome_feature_list_h/Bromite-auto-updater.inc 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 @@ -83,6 +83,8 @@ public class BuildConfig { public static boolean CRONET_FOR_AOSP_BUILD; #endif + public static String BUILD_TARGET_CPU = QUOTE(_BUILD_TARGET_CPU); + #if defined(_WRITE_CLANG_PROFILING_DATA) public static boolean WRITE_CLANG_PROFILING_DATA = true; #else 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 @@ -1980,6 +1980,9 @@ if (!is_robolectric && enable_java_templates) { sources = [ "//build/android/java/templates/BuildConfig.template" ] defines = [] + # add arch to org.chromium.build.BuildConfig + defines += [ "_BUILD_TARGET_CPU=${target_cpu}" ] + if ((defined(invoker.assertions_implicitly_enabled) && invoker.assertions_implicitly_enabled) || enable_java_asserts) { defines += [ "_ENABLE_ASSERTS" ] 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 @@ -921,6 +921,8 @@ chrome_java_sources = [ "java/src/org/chromium/chrome/browser/omaha/OmahaBase.java", "java/src/org/chromium/chrome/browser/omaha/OmahaDelegate.java", "java/src/org/chromium/chrome/browser/omaha/OmahaDelegateBase.java", + "java/src/org/chromium/chrome/browser/omaha/CromiteUpdateStatusProvider.java", + "java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java", "java/src/org/chromium/chrome/browser/omaha/OmahaService.java", "java/src/org/chromium/chrome/browser/omaha/RequestData.java", "java/src/org/chromium/chrome/browser/omaha/RequestGenerator.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 @@ -9,6 +9,11 @@ found in the LICENSE file. + 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 @@ -32,10 +32,15 @@ import org.chromium.ui.widget.Toast; import java.util.Calendar; +import android.content.SharedPreferences; +import org.chromium.chrome.browser.omaha.OmahaPrefUtils; +import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; + /** Settings fragment that displays information about Chrome. */ @NullMarked public class AboutChromeSettings extends ChromeBaseSettingsFragment - implements EmbeddableSettingsPage, Preference.OnPreferenceClickListener { + implements EmbeddableSettingsPage, Preference.OnPreferenceClickListener, + Preference.OnPreferenceChangeListener { static { CalendarFactory.warmUp(); } @@ -43,6 +48,7 @@ public class AboutChromeSettings extends ChromeBaseSettingsFragment private static final int TAPS_FOR_DEVELOPER_SETTINGS = 7; private static final String PREF_APPLICATION_VERSION = "application_version"; + private static final String PREF_ALLOW_INLINE_UPDATE = "allow_inline_update"; // switch preference private static final String PREF_OS_VERSION = "os_version"; private static final String PREF_LEGAL_INFORMATION = "legal_information"; @@ -82,6 +88,13 @@ public class AboutChromeSettings extends ChromeBaseSettingsFragment calendar.setTimeInMillis(System.currentTimeMillis()); int currentYear = calendar.get(Calendar.YEAR); p.setSummary(getString(R.string.legal_information_summary, currentYear)); + + ChromeSwitchPreference allowInlineUpdate = + (ChromeSwitchPreference) findPreference(PREF_ALLOW_INLINE_UPDATE); + allowInlineUpdate.setChecked( + OmahaPrefUtils.getSharedPreferences() + .getBoolean(OmahaPrefUtils.PREF_ALLOW_INLINE_UPDATE, false)); + allowInlineUpdate.setOnPreferenceChangeListener(this); } @Override @@ -152,6 +165,19 @@ public class AboutChromeSettings extends ChromeBaseSettingsFragment return true; } + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String key = preference.getKey(); + if (PREF_ALLOW_INLINE_UPDATE.equals(key)) { + SharedPreferences.Editor sharedPreferenceEditor = OmahaPrefUtils.getSharedPreferences().edit(); + sharedPreferenceEditor.putBoolean(OmahaPrefUtils.PREF_ALLOW_INLINE_UPDATE, (boolean) newValue); + sharedPreferenceEditor.apply(); + + OmahaPrefUtils.resetUpdatePrefs(); + } + return true; + } + @Override public @SettingsFragment.AnimationType int getAnimationType() { return SettingsFragment.AnimationType.PROPERTY; diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/CromiteUpdateStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/CromiteUpdateStatusProvider.java new file mode 100644 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/CromiteUpdateStatusProvider.java @@ -0,0 +1,42 @@ +package org.chromium.chrome.browser.omaha; + +import android.app.Activity; + +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; + +import org.chromium.chrome.browser.app.ChromeActivity; + +import org.chromium.chrome.browser.omaha.UpdateStatusProvider; +import org.chromium.chrome.browser.omaha.inline.BromiteInlineUpdateController; + +public class CromiteUpdateStatusProvider extends UpdateStatusProvider { + private static final class LazyHolder { + private static final UpdateStatusProvider INSTANCE = new CromiteUpdateStatusProvider(); + } + + private CromiteUpdateStatusProvider() { + super(new BromiteInlineUpdateController()); + } + + /** @return Returns a singleton of {@link UpdateStatusProvider}. */ + public static UpdateStatusProvider getInstance() { + return LazyHolder.INSTANCE; + } + + // ApplicationStateListener implementation. + @Override + public void onActivityStateChange(Activity changedActivity, @ActivityState int newState) { + boolean hasActiveActivity = false; + + for (Activity activity : ApplicationStatus.getRunningActivities()) { + if (activity == null || !(activity instanceof ChromeActivity)) continue; + + hasActiveActivity |= + ApplicationStatus.getStateForActivity(activity) == ActivityState.RESUMED; + if (hasActiveActivity) break; + } + + mInlineController.setEnabled(hasActiveActivity); + } +} 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 @@ -149,7 +149,8 @@ public class OmahaBase { } static boolean isDisabled() { - return sDisabledForTesting; + // do not enable version control via Omaha Update Server + return true; } /** @@ -592,6 +593,10 @@ public class OmahaBase { /** Sends the request to the server and returns the response. */ static String sendRequestToServer(HttpURLConnection urlConnection, String request) throws RequestFailureException { + if ((true)) { + throw new RequestFailureException("Requests to Omaha server are forbidden.", + RequestFailureException.ERROR_CONNECTIVITY); + } try { OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); OutputStreamWriter writer = new OutputStreamWriter(out); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java @@ -9,6 +9,7 @@ import android.content.ActivityNotFoundException; import android.content.res.Resources; import android.text.TextUtils; import android.view.Choreographer; +import androidx.annotation.StringRes; import org.chromium.base.ApkInfo; import org.chromium.base.Callback; @@ -23,6 +24,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState; import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus; +import org.chromium.chrome.browser.omaha.CromiteUpdateStatusProvider; import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.ProfileKeyedMap; @@ -115,7 +117,7 @@ public class UpdateMenuItemHelper { return; } - UpdateStatusProvider.getInstance().addObserver(mUpdateCallback); + CromiteUpdateStatusProvider.getInstance().addObserver(mUpdateCallback); } /** Unregisters {@code observer} from menu state changes. */ @@ -140,14 +142,27 @@ public class UpdateMenuItemHelper { if (TextUtils.isEmpty(mStatus.updateUrl)) return; try { - UpdateStatusProvider.getInstance() + CromiteUpdateStatusProvider.getInstance() .startIntentUpdate(activity, /* newTask= */ false); } catch (ActivityNotFoundException e) { Log.e(TAG, "Failed to launch Activity for: %s", mStatus.updateUrl); } break; + case UpdateState.VULNERABLE_VERSION: + // Intentional fall through. + case UpdateState.INLINE_UPDATE_AVAILABLE: + CromiteUpdateStatusProvider.getInstance().startInlineUpdate(activity); + break; + case UpdateState.INLINE_UPDATE_READY: + CromiteUpdateStatusProvider.getInstance().finishInlineUpdate(); + break; + case UpdateState.INLINE_UPDATE_FAILED: + CromiteUpdateStatusProvider.getInstance().retryInlineUpdate(activity); + break; case UpdateState.UNSUPPORTED_OS_VERSION: // Intentional fall through. + case UpdateState.INLINE_UPDATE_DOWNLOADING: + // Intentional fall through. default: return; } @@ -183,7 +198,7 @@ public class UpdateMenuItemHelper { if (mStatus == null) return; if (mStatus.updateState != UpdateState.UNSUPPORTED_OS_VERSION) return; - UpdateStatusProvider.getInstance().updateLatestUnsupportedVersion(); + CromiteUpdateStatusProvider.getInstance().updateLatestUnsupportedVersion(); } private void handleStateChanged() { @@ -196,7 +211,7 @@ public class UpdateMenuItemHelper { mMenuUiState = new MenuUiState(); switch (mStatus.updateState) { - case UpdateState.UPDATE_AVAILABLE: + case UpdateState.UPDATE_AVAILABLE: // this is not used in Bromite // The badge is hidden if the update menu item has been clicked until there is an // even newer version of Chrome available. showBadge |= @@ -254,6 +269,72 @@ public class UpdateMenuItemHelper { mMenuUiState.itemState.icon = R.drawable.ic_error_24dp_filled; mMenuUiState.itemState.enabled = false; break; + case UpdateState.VULNERABLE_VERSION: + // Intentional fall through. + case UpdateState.INLINE_UPDATE_AVAILABLE: + // The badge is hidden if the update menu item has been clicked until there is an + // even newer version of Chrome available. + @StringRes int defaultUpdateSummary = R.string.menu_update_summary_default; + if (mStatus.updateState == UpdateState.VULNERABLE_VERSION) { + // always show badge in case of vulnerable version + showBadge = true; + mMenuUiState.buttonState = new MenuButtonState(); + mMenuUiState.buttonState.menuContentDescription = R.string.accessibility_toolbar_btn_menu_update; + mMenuUiState.buttonState.darkBadgeIcon = + R.drawable.ic_error_grey800_24dp_filled; + mMenuUiState.buttonState.lightBadgeIcon = R.drawable.ic_error_white_24dp_filled; + mMenuUiState.buttonState.adaptiveBadgeIcon = R.drawable.ic_error_24dp_filled; + defaultUpdateSummary = R.string.menu_update_summary_vulnerable; + } else { + showBadge |= !TextUtils.equals( + getPrefService().getString( + Pref.LATEST_VERSION_WHEN_CLICKED_UPDATE_MENU_ITEM), + mStatus.latestUnsupportedVersion); + if (showBadge) { + mMenuUiState.buttonState = new MenuButtonState(); + mMenuUiState.buttonState.menuContentDescription = R.string.accessibility_toolbar_btn_menu_update; + mMenuUiState.buttonState.darkBadgeIcon = R.drawable.badge_update_dark; + mMenuUiState.buttonState.lightBadgeIcon = R.drawable.badge_update_light; + mMenuUiState.buttonState.adaptiveBadgeIcon = R.drawable.badge_update; + } + } + + mMenuUiState.itemState = new MenuItemState(); + mMenuUiState.itemState.title = R.string.menu_update; + mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; + mMenuUiState.itemState.summary = UpdateConfigs.getCustomSummary(); + if (TextUtils.isEmpty(mMenuUiState.itemState.summary)) { + mMenuUiState.itemState.summary = + resources.getString(defaultUpdateSummary); + } + mMenuUiState.itemState.icon = R.drawable.ic_history_24dp; + mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; + mMenuUiState.itemState.enabled = true; + break; + case UpdateState.INLINE_UPDATE_DOWNLOADING: + mMenuUiState.itemState = new MenuItemState(); + mMenuUiState.itemState.title = R.string.menu_inline_update_downloading; + mMenuUiState.itemState.titleColorId = R.color.default_text_color_secondary_dark; + break; + case UpdateState.INLINE_UPDATE_READY: + mMenuUiState.itemState = new MenuItemState(); + mMenuUiState.itemState.title = R.string.menu_inline_update_ready; + mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; + mMenuUiState.itemState.summary = + resources.getString(R.string.menu_inline_update_ready_summary); + mMenuUiState.itemState.icon = R.drawable.infobar_chrome; + mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; + mMenuUiState.itemState.enabled = true; + break; + case UpdateState.INLINE_UPDATE_FAILED: + mMenuUiState.itemState = new MenuItemState(); + mMenuUiState.itemState.title = R.string.menu_inline_update_failed; + mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; + mMenuUiState.itemState.summary = resources.getString(R.string.try_again); + mMenuUiState.itemState.icon = R.drawable.ic_history_24dp; + mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; + mMenuUiState.itemState.enabled = true; + break; case UpdateState.NONE: // Intentional fall through. default: 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,285 @@ +// 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 . + +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.base.task.TaskTraits; +import org.chromium.chrome.browser.app.ChromeActivity; +import org.chromium.chrome.browser.omaha.OmahaBase; +import org.chromium.chrome.browser.omaha.OmahaPrefUtils; +import org.chromium.chrome.browser.omaha.UpdateConfigs; +import org.chromium.chrome.browser.omaha.UpdateStatusProvider; +import org.chromium.chrome.browser.omaha.VersionNumber; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.profiles.ProfileManager; +import org.chromium.chrome.browser.tab.TabLaunchType; +import org.chromium.chrome.browser.tabmodel.TabCreator; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.ui.base.PageTransition; +import org.chromium.net.NetworkTrafficAnnotationTag; + +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; + +public class BromiteInlineUpdateController implements InlineUpdateController { + + private static final String TAG = "BromiteInlineUpdateController"; + private final String REDIRECT_URL_PREFIX = "https://github.com/bromite/bromite/releases/download/"; + private static final String UPDATE_VERSION_URL = "https://github.com/bromite/bromite/releases/latest/download/"; + private final String UPSTREAM_VERSION_URL = "https://www.bromite.org/upstream.txt"; + public static final String VULNERABLE_VERSION_DOC_URL = "https://www.bromite.org/vulnerable-version"; + + @Override + public String getDownloadUrl() { + return UPDATE_VERSION_URL + BuildConfig.BUILD_TARGET_CPU + "_ChromePublic.apk"; + } + + @Override + public String getVulnerableVersionDocUrl() { + return VULNERABLE_VERSION_DOC_URL; + } + + private static final NetworkTrafficAnnotationTag TRAFFIC_ANNOTATION = + NetworkTrafficAnnotationTag.createComplete("bromite_inline_update_controller", + "semantics {" + + " sender: 'Bromite Inline Update (Android)'" + + " description:" + + " 'Check for update'" + + " trigger: 'This request is made once, on first run'" + + " data: 'None.'" + + " destination: OTHER" + + " internal {" + + " contacts {" + + " email: 'uazo@users.noreply.github.com'" + + " }" + + " contacts {" + + " email: 'uazo@users.noreply.github.com'" + + " }" + + " }" + + " user_data {" + + " type: NONE" + + " }" + + " last_reviewed: '2023-01-01'" + + "}" + + "policy {" + + " cookies_allowed: NO" + + " setting: 'Can be disabled in Settings.'" + + " policy_exception_justification: 'Not implemented.'" + + "}"); + + private boolean mEnabled = true; + private Runnable mCallback; + private @Nullable @UpdateStatusProvider.UpdateState Integer mUpdateState = + UpdateStatusProvider.UpdateState.NONE; + private String mUpdateUrl = ""; + + public BromiteInlineUpdateController() {} + + @Override + public void setCallback(Runnable callback) { + mCallback = callback; + } + + @Override + public void setEnabled(boolean enabled) { + if (mEnabled == enabled) return; + + mEnabled = enabled; + // check for an update when state changes + if (mEnabled) pullCurrentState(); + } + + @Override + public @Nullable @UpdateStatusProvider.UpdateState Integer getStatus() { + if (mEnabled) pullCurrentState(); + return mUpdateState; + } + + @Override + public String getUpdateUrl() { + // relies on a prior call to getStatus() to have state and URL correctly pulled + return mUpdateUrl; + } + + @Override + public void startUpdate(Activity activity) { + assert ChromeActivity.class.isInstance(activity); + ChromeActivity thisActivity = (ChromeActivity) activity; + // Always open in new incognito tab + TabCreator tabCreator = thisActivity.getTabCreator(true); + tabCreator.createNewTab(new LoadUrlParams(mUpdateUrl, PageTransition.AUTO_BOOKMARK), + TabLaunchType.FROM_LINK, thisActivity.getActivityTab()); + } + + @Override + public void completeUpdate() { + } + + private void pullCurrentState() { + if (OmahaPrefUtils.getSharedPreferences() + .getBoolean(OmahaPrefUtils.PREF_ALLOW_INLINE_UPDATE, false) == false) { + Log.i(TAG, "BromiteUpdater: disabled by user"); + return; + } + + // do not pull state if there is already a state set + if (mUpdateState != UpdateStatusProvider.UpdateState.NONE) + return; + + if (shallUpdate() == false) + return; + + switch (mUpdateState) { + case UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE: + break; + case UpdateStatusProvider.UpdateState.NONE: + OmahaPrefUtils.resetUpdatePrefs(); + checkLatestVersion((latestVersion) -> { + if (latestVersion == null) return; + + if (OmahaPrefUtils.isNewVersionAvailableByVersion(latestVersion)) { + postStatus(UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE, getDownloadUrl()); + } else { + checkLatestUpstreamVersion((latestUpstreamVersion) -> { + if (latestUpstreamVersion == null) return; + if (OmahaPrefUtils.isNewVersionAvailableByVersion(latestUpstreamVersion)) { + postStatus(UpdateStatusProvider.UpdateState.VULNERABLE_VERSION, VULNERABLE_VERSION_DOC_URL); + } + }); + } + }); + 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. + case UpdateStatusProvider.UpdateState.VULNERABLE_VERSION: + // Intentional fall through. + default: + return; + } + } + + private boolean shallUpdate() { + long currentTime = System.currentTimeMillis(); + SharedPreferences preferences = OmahaPrefUtils.getSharedPreferences(); + long lastPushedTimeStamp = preferences.getLong(OmahaPrefUtils.PREF_TIMESTAMP_OF_REQUEST, 0); + return currentTime - lastPushedTimeStamp >= getUpdateNotificationInterval(); + } + + private void checkLatestVersion(final Callback callback) { + assert UPDATE_VERSION_URL != null; + + String urlToCheck = getDownloadUrl(); + Log.i(TAG, "BromiteUpdater: fetching with HEAD '%s'", urlToCheck); + + EndpointFetcher.nativeHeadWithNoAuth( + (endpointResponse) -> { + boolean versionFound = false; + String redirectURL = endpointResponse.getRedirectUrl(); + if (redirectURL != null) { + Log.i(TAG, "BromiteUpdater: obtained response '%s' and redirect URL '%s'", endpointResponse.getResponseString(), redirectURL); + if (redirectURL.indexOf(REDIRECT_URL_PREFIX) == 0) { + redirectURL = redirectURL.substring(REDIRECT_URL_PREFIX.length()); + String[] parts = redirectURL.split(Pattern.quote("/")); + if (parts.length > 0) { + VersionNumber version = VersionNumber.fromString(parts[0]); + if (version != null) { + versionFound = true; + if (UPSTREAM_VERSION_URL.equals("")) + OmahaPrefUtils.updateLastPushedTimeStamp(System.currentTimeMillis()); + OmahaPrefUtils.setLatestModifiedVersion(parts[0]); + callback.onResult(version); + return; + } + } + } + } + if (!versionFound) { + // retry after 1 hour + OmahaPrefUtils.updateLastPushedTimeStamp( + System.currentTimeMillis() - getUpdateNotificationInterval() - + DateUtils.HOUR_IN_MILLIS); + Log.e(TAG, "BromiteUpdater: failed, will retry in 1 hour"); + } + + callback.onResult(null); + }, + ProfileManager.getLastUsedRegularProfile(), + urlToCheck, /*timeout*/5000, /*follow_redirect*/true, TRAFFIC_ANNOTATION); + } + + private void checkLatestUpstreamVersion(final Callback callback) { + Log.i(TAG, "BromiteUpdater: fetching with GET '%s'", UPSTREAM_VERSION_URL); + + EndpointFetcher.nativeFetchWithNoAuth( + (endpointResponse) -> { + String response = endpointResponse.getResponseString().trim(); + Log.i(TAG, "BromiteUpdater: obtained upstream version update response '%s'", response); + VersionNumber version = VersionNumber.fromString(response); + if (version != null) { + OmahaPrefUtils.updateLastPushedTimeStamp(System.currentTimeMillis()); + OmahaPrefUtils.setLatestUpstreamVersion(response); + callback.onResult(version); + return; + } + // retry after 1 hour + OmahaPrefUtils.updateLastPushedTimeStamp( + System.currentTimeMillis() - getUpdateNotificationInterval() - + DateUtils.HOUR_IN_MILLIS); + Log.e(TAG, "BromiteUpdater: failed to fetch upstream version, will retry in 1 hour"); + + callback.onResult(null); + }, + ProfileManager.getLastUsedRegularProfile(), + UPSTREAM_VERSION_URL, /*timeout*/5000, /*follow_redirect*/false, TRAFFIC_ANNOTATION); + } + + private void postStatus(@UpdateStatusProvider.UpdateState int status, String updateUrl) { + mUpdateState = status; + mUpdateUrl = updateUrl; + PostTask.postTask(TaskTraits.UI_DEFAULT, mCallback); + } +} diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -4383,6 +4383,16 @@ static_library("browser") { } } + # static_library("browser") + if (is_android) { + sources += [ + "endpoint_fetcher/endpoint_fetcher_android.cc", + ] + deps += [ + "//chrome/browser/endpoint_fetcher:jni_headers", + ] + } + if (enable_screen_capture) { sources += [ "media/capture_access_handler_base.cc", diff --git a/chrome/browser/endpoint_fetcher/BUILD.gn b/chrome/browser/endpoint_fetcher/BUILD.gn new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright 2020 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") +import("//third_party/jni_zero/jni_zero.gni") + +android_library("java") { + deps = [ + ":jni_headers", + "//base:base_java", + "//build/android:build_java", + "//chrome/browser/profiles/android:java", + "//net/android:net_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/jni_zero:jni_zero_java", + ] + srcjar_deps = [ ":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", + ] +} + +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/DEPS b/chrome/browser/endpoint_fetcher/DEPS new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/endpoint_fetcher", +] diff --git a/chrome/browser/endpoint_fetcher/OWNERS b/chrome/browser/endpoint_fetcher/OWNERS new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/OWNERS @@ -0,0 +1 @@ +file://chrome/browser/complex_tasks/OWNERS diff --git a/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc b/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc @@ -0,0 +1,109 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/endpoint_fetcher/endpoint_fetcher.h" + +#include "base/android/callback_android.h" +#include "base/android/jni_array.h" +#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.h" +#include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/common/channel_info.h" +#include "components/signin/public/base/consent_level.h" +#include "components/version_info/channel.h" +#include "content/public/browser/storage_partition.h" + +namespace { +static void OnEndpointFetcherComplete( + const base::android::JavaRef& 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 endpoint_fetcher, + std::unique_ptr endpoint_response) { + base::android::RunObjectCallbackAndroid( + jcaller, Java_EndpointResponse_createEndpointResponse( + base::android::AttachCurrentThread(), + base::android::ConvertUTF8ToJavaString( + base::android::AttachCurrentThread(), + std::move(endpoint_response->response)))); +} + +static void OnEndpointFetcherHeadComplete( + const base::android::JavaRef& 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 endpoint_fetcher, + std::unique_ptr 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 + +static void JNI_EndpointFetcher_NativeFetchWithNoAuth( + JNIEnv* env, + const base::android::JavaRef& jprofile, + const base::android::JavaRef& jurl, + jlong jtimeout, jboolean intercept_redirect, + jint jannotation_hash_code, + const base::android::JavaRef& jcallback) { + auto endpoint_fetcher = std::make_unique( + Profile::FromJavaObject(jprofile) + ->GetDefaultStoragePartition() + ->GetURLLoaderFactoryForBrowserProcess(), + GURL(base::android::ConvertJavaStringToUTF8(env, jurl)), + "GET", + jtimeout, + intercept_redirect, + net::NetworkTrafficAnnotationTag::FromJavaAnnotation( + jannotation_hash_code)); + auto* const endpoint_fetcher_ptr = endpoint_fetcher.get(); + endpoint_fetcher_ptr->PerformRequest( + base::BindOnce(&OnEndpointFetcherComplete, + base::android::ScopedJavaGlobalRef(jcallback), + // unique_ptr endpoint_fetcher is passed until the callback + // to ensure its lifetime across the request. + std::move(endpoint_fetcher)), + nullptr); +} + +static void JNI_EndpointFetcher_NativeHeadWithNoAuth( + JNIEnv* env, + const base::android::JavaRef& jprofile, + const base::android::JavaRef& jurl, + jlong jtimeout, jboolean intercept_redirect, + jint jannotation_hash_code, + const base::android::JavaRef& jcallback) { + auto endpoint_fetcher = std::make_unique( + Profile::FromJavaObject(jprofile) + ->GetDefaultStoragePartition() + ->GetURLLoaderFactoryForBrowserProcess(), + GURL(base::android::ConvertJavaStringToUTF8(env, jurl)), + "HEAD", + jtimeout, + intercept_redirect, + net::NetworkTrafficAnnotationTag::FromJavaAnnotation( + jannotation_hash_code)); + auto* const endpoint_fetcher_ptr = endpoint_fetcher.get(); + endpoint_fetcher_ptr->PerformRequest( + base::BindOnce(&OnEndpointFetcherHeadComplete, + base::android::ScopedJavaGlobalRef(jcallback), + // unique_ptr endpoint_fetcher is passed until the callback + // to ensure its lifetime across the request. + std::move(endpoint_fetcher)), + nullptr); +} + +DEFINE_JNI(EndpointFetcher) 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 new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java @@ -0,0 +1,63 @@ +// Copyright 2019 The Chromium Authors +// 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 androidx.annotation.MainThread; + +import org.jni_zero.NativeMethods; + +import org.chromium.base.Callback; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.net.NetworkTrafficAnnotationTag; + +/** + * EndpointFetcher uses native EndpointFetcher to call a HTTPS endpoint and return + * the response. The call to native EndpointFetcher is achieved over a static call + * over JNI. The native EndpointFetcher is created during the static call and + * destroyed in the callback function. + * EndpointFetcher currently doesn't support incognito mode. + * If the request times out an empty response will be returned. There will also + * be an error code indicating timeout once more detailed error messaging is added + * TODO(crbug.com/993393). + */ +public final class EndpointFetcher { + private EndpointFetcher() {} + + @MainThread + public static void nativeHeadWithNoAuth( + Callback callback, Profile profile, + String url, long timeout, boolean allow_redirect, + NetworkTrafficAnnotationTag annotation) { + EndpointFetcherJni.get().nativeHeadWithNoAuth( + profile, url, timeout, allow_redirect, annotation.getHashCode(), callback); + } + + @MainThread + public static void nativeFetchWithNoAuth( + Callback callback, Profile profile, + String url, long timeout, boolean allow_redirect, + NetworkTrafficAnnotationTag annotation) { + EndpointFetcherJni.get().nativeFetchWithNoAuth( + profile, url, timeout, allow_redirect, annotation.getHashCode(), callback); + } + + @NativeMethods + public interface Natives { + void nativeFetchWithNoAuth( + Profile profile, + String url, + long timeout, + boolean allow_redirect, + int annotationHashCode, + Callback callback); + void nativeHeadWithNoAuth( + Profile profile, + String url, + long timeout, + boolean allow_redirect, + int annotationHashCode, + Callback 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.jni_zero.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/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java new file mode 100644 --- /dev/null +++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors +// 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.jni_zero.CalledByNative; + +/** Encapsulates the response from the {@Link EndpointFetcher} */ +public class EndpointResponse { + private final String mResponseString; + + /** + * Create the EndpointResponse + * @param responseString the response string acquired from the endpoint + */ + public EndpointResponse(String responseString) { + mResponseString = responseString; + } + + /** Response string acquired from calling an endpoint */ + public String getResponseString() { + return mResponseString; + } + + @CalledByNative + private static EndpointResponse createEndpointResponse(String response) { + return new EndpointResponse(response); + } +} 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 @@ -420,6 +420,7 @@ const base::Feature* const kFeaturesExposedToJava[] = { &kProtectedTabsAndroid, &kPwaRestoreUi, &kPwaRestoreUiAtStartup, + &kInlineUpdateFlow, &kReadAloudAudioOverviews, &kReadAloudIPHMenuButtonHighlightCCT, &kReadAloudPlayback, diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java @@ -504,6 +504,7 @@ public abstract class ChromeFeatureList { public static final String INCOGNITO_NTP_SMALL_ICON = "IncognitoNtpSmallIcon"; public static final String INCOGNITO_SCREENSHOT = "IncognitoScreenshot"; public static final String INCOGNITO_THEME_OVERLAY_TESTING = "IncognitoThemeOverlayTesting"; + public static final String INLINE_UPDATE_FLOW = "InlineUpdateFlow"; public static final String INLINE_PDF_V2 = "InlinePdfV2"; public static final String KEYBOARD_ESC_BACK_NAVIGATION = "KeyboardEscBackNavigation"; public static final String LAUNCH_CAUSE_SCREEN_OFF_FIX = "LaunchCauseScreenOffFix"; diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc --- a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc +++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc @@ -265,7 +265,7 @@ AccessCodeCastDiscoveryInterface::CreateEndpointFetcher( .SetTimeout(kTimeout) .SetUrl(GURL(base::StrCat({GetDiscoveryUrl(), "/", access_code}))) .SetPostData(kEmptyPostData) - .Build()); + .Build(), kTrafficAnnotation); } void AccessCodeCastDiscoveryInterface::ValidateDiscoveryAccessCode( diff --git a/chrome/browser/omaha/android/BUILD.gn b/chrome/browser/omaha/android/BUILD.gn --- a/chrome/browser/omaha/android/BUILD.gn +++ b/chrome/browser/omaha/android/BUILD.gn @@ -19,6 +19,9 @@ android_library("java") { "java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java", "java/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetrics.java", ] + sources += [ + "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java", + ] deps = [ ":update_proto_java", "//base:base_java", diff --git a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/OmahaPrefUtils.java b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/OmahaPrefUtils.java --- a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/OmahaPrefUtils.java +++ b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/OmahaPrefUtils.java @@ -7,8 +7,10 @@ package org.chromium.chrome.browser.omaha; import android.content.Context; import android.content.SharedPreferences; +import org.chromium.base.Log; import org.chromium.base.ContextUtils; import org.chromium.build.annotations.NullMarked; +import org.chromium.base.version_info.VersionInfo; @NullMarked public class OmahaPrefUtils { @@ -24,7 +26,55 @@ public class OmahaPrefUtils { 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"; + static final String PREF_LATEST_MODIFIED_VERSION = "latestModifiedVersion"; + static final String PREF_LATEST_UPSTREAM_VERSION = "latestUpstreamVersion"; + public static final String PREF_ALLOW_INLINE_UPDATE = "allowInlineUpdate"; + + static final String TAG = "omaha_utils"; + + public static boolean isNewVersionAvailableByVersion(VersionNumber latestVersion) { + VersionNumber mCurrentProductVersion = VersionNumber.fromString(VersionInfo.getProductVersion()); + if (mCurrentProductVersion == null) { + Log.e(TAG, "BromiteUpdater: current product version is null"); + return false; + } + + Log.i(TAG, "BromiteUpdater: currentProductVersion=%s, latestVersion=%s", + mCurrentProductVersion.toString(), latestVersion.toString()); + + return mCurrentProductVersion.isSmallerThan(latestVersion); + } + + public static void updateLastPushedTimeStamp(long timeMillis) { + SharedPreferences preferences = getSharedPreferences(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putLong(PREF_TIMESTAMP_OF_REQUEST, timeMillis); + editor.apply(); + } + + public static void setLatestModifiedVersion(String version) { + SharedPreferences preferences = getSharedPreferences(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString(PREF_LATEST_MODIFIED_VERSION, version); + editor.apply(); + } + + public static void setLatestUpstreamVersion(String version) { + SharedPreferences preferences = getSharedPreferences(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString(PREF_LATEST_UPSTREAM_VERSION, version); + editor.apply(); + } + + public static void resetUpdatePrefs() { + SharedPreferences preferences = getSharedPreferences(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putLong(PREF_TIMESTAMP_OF_REQUEST, 0); + editor.putString(PREF_LATEST_MODIFIED_VERSION, ""); + editor.putString(PREF_LATEST_UPSTREAM_VERSION, ""); + editor.apply(); + } /** Returns the Omaha SharedPreferences. */ public static SharedPreferences getSharedPreferences() { diff --git a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java --- a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java +++ b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java @@ -12,6 +12,7 @@ import androidx.annotation.IntDef; import org.chromium.base.CommandLine; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState; import org.chromium.components.variations.VariationsAssociatedData; @@ -38,10 +39,12 @@ public class UpdateConfigs { private static final String UPDATE_AVAILABLE_SWITCH_VALUE = "update_available"; private static final String UNSUPPORTED_OS_VERSION_SWITCH_VALUE = "unsupported_os_version"; + private static final long DEFAULT_UPDATE_NOTIFICATION_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS; private static final long DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS = 2 * DateUtils.DAY_IN_MILLIS; /** Possible update flow configurations. */ - @IntDef({UpdateFlowConfiguration.NEVER_SHOW, UpdateFlowConfiguration.INTENT_ONLY}) + @IntDef({UpdateFlowConfiguration.NEVER_SHOW, UpdateFlowConfiguration.INTENT_ONLY, + UpdateFlowConfiguration.INLINE_ONLY}) @Retention(RetentionPolicy.SOURCE) public @interface UpdateFlowConfiguration { /** Turns off all update indicators. */ @@ -51,6 +54,12 @@ public class UpdateConfigs { * Requires Omaha to say an update is available, and only ever Intents out to Play Store. */ int INTENT_ONLY = 2; + + /** + * Inline updates that contact Bromite official GitHub repository to say whether an update is available. + * Only ever uses the inline update flow. + */ + int INLINE_ONLY = 3; } /** @@ -126,6 +135,13 @@ public class UpdateConfigs { return DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS; } + /** + * @return A time interval for scheduling update notification. Unit: mills. + */ + public static long getUpdateNotificationInterval() { + return DEFAULT_UPDATE_NOTIFICATION_INTERVAL; + } + /** * Gets a String VariationsAssociatedData parameter. Also checks for a command-line switch * with the same name, for easy local testing. @@ -139,4 +155,13 @@ public class UpdateConfigs { } return value; } + + @UpdateFlowConfiguration + 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.INLINE_ONLY; + } + return UpdateFlowConfiguration.NEVER_SHOW; + } } diff --git a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java --- a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java +++ b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java @@ -6,6 +6,7 @@ package org.chromium.chrome.browser.omaha; import static org.chromium.build.NullUtil.assumeNonNull; +import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -15,9 +16,11 @@ import android.os.StatFs; import android.text.TextUtils; import androidx.annotation.IntDef; +import androidx.annotation.VisibleForTesting; -import com.google.android.gms.common.GooglePlayServicesUtil; - +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.ApkInfo; import org.chromium.base.Callback; import org.chromium.base.DeviceInfo; @@ -31,6 +34,7 @@ import org.chromium.base.task.TaskTraits; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.build.annotations.RequiresNonNull; +import org.chromium.chrome.browser.omaha.inline.InlineUpdateController; import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; @@ -40,6 +44,11 @@ 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; + /** * Provides the current update state for Chrome. This update state is asynchronously determined and * can change as Chrome runs. @@ -47,24 +56,26 @@ import java.lang.annotation.RetentionPolicy; * For manually testing this functionality, see {@link UpdateConfigs}. */ @NullMarked -public class UpdateStatusProvider { +public abstract class UpdateStatusProvider implements ActivityStateListener { /** * Possible update states. * Treat this as append only as it is used by UMA. */ - @IntDef({UpdateState.NONE, UpdateState.UPDATE_AVAILABLE, UpdateState.UNSUPPORTED_OS_VERSION}) + @IntDef({UpdateState.NONE, UpdateState.UPDATE_AVAILABLE, UpdateState.UNSUPPORTED_OS_VERSION, + UpdateState.INLINE_UPDATE_AVAILABLE, UpdateState.INLINE_UPDATE_DOWNLOADING, + UpdateState.INLINE_UPDATE_READY, UpdateState.INLINE_UPDATE_FAILED, UpdateState.VULNERABLE_VERSION}) @Retention(RetentionPolicy.SOURCE) public @interface UpdateState { int NONE = 0; int UPDATE_AVAILABLE = 1; int UNSUPPORTED_OS_VERSION = 2; - // Inline updates are deprecated. - // int INLINE_UPDATE_AVAILABLE = 3; - // int INLINE_UPDATE_DOWNLOADING = 4; - // int INLINE_UPDATE_READY = 5; - // int INLINE_UPDATE_FAILED = 6; + int INLINE_UPDATE_AVAILABLE = 3; + int INLINE_UPDATE_DOWNLOADING = 4; + int INLINE_UPDATE_READY = 5; + int INLINE_UPDATE_FAILED = 6; + int VULNERABLE_VERSION = 7; - int NUM_ENTRIES = 7; + int NUM_ENTRIES = 8; } /** A set of properties that represent the current update state for Chrome. */ @@ -98,6 +109,12 @@ public class UpdateStatusProvider { */ private boolean mIsSimulated; + /** + * Whether or not we are currently trying to simulate an inline flow. Used to allow + * overriding Omaha update state, which usually supersedes inline update states. + */ + private boolean mIsInlineSimulated; + public UpdateStatus() {} UpdateStatus(UpdateStatus other) { @@ -106,11 +123,13 @@ public class UpdateStatusProvider { latestVersion = other.latestVersion; latestUnsupportedVersion = other.latestUnsupportedVersion; mIsSimulated = other.mIsSimulated; + mIsInlineSimulated = other.mIsInlineSimulated; } } private final ObserverList> mObservers = new ObserverList<>(); + protected final InlineUpdateController mInlineController; private final UpdateQuery mOmahaQuery; private final UpdateSuccessMetrics mMetrics; private @Nullable UpdateStatus mStatus; @@ -118,11 +137,6 @@ public class UpdateStatusProvider { /** Whether or not we've recorded the initial update status yet. */ private boolean mRecordedInitialStatus; - /** @return Returns a singleton of {@link UpdateStatusProvider}. */ - public static UpdateStatusProvider getInstance() { - return LazyHolder.INSTANCE; - } - /** * Adds {@code observer} to notify about update state changes. It is safe to call this multiple * times with the same {@code observer}. This method will always notify {@code observer} of the @@ -178,6 +192,30 @@ public class UpdateStatusProvider { pingObservers(); } + /** + * Starts the inline update process, if possible. + * @param activity An {@link Activity} that will be used to interact with Play. + */ + public void startInlineUpdate(Activity activity) { + if (mStatus == null || (mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE && mStatus.updateState != UpdateState.VULNERABLE_VERSION)) return; + mInlineController.startUpdate(activity); + } + + /** + * Retries the inline update process, if possible. + * @param activity An {@link Activity} that will be used to interact with Play. + */ + public void retryInlineUpdate(Activity activity) { + if (mStatus == null || (mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE && mStatus.updateState != UpdateState.VULNERABLE_VERSION)) return; + mInlineController.startUpdate(activity); + } + + /** Finishes the inline update process, which may involve restarting the app. */ + public void finishInlineUpdate() { + if (mStatus == null || mStatus.updateState != UpdateState.INLINE_UPDATE_READY) return; + mInlineController.completeUpdate(); + } + /** * Starts the intent update process, if possible * @param context An {@link Context} that will be used to fire off the update intent. @@ -185,12 +223,11 @@ public class UpdateStatusProvider { * @return Whether or not the update intent was sent and had a valid handler. */ public boolean startIntentUpdate(Context context, boolean newTask) { + // currently not used in Bromite if (mStatus == null || mStatus.updateState != UpdateState.UPDATE_AVAILABLE) return false; if (TextUtils.isEmpty(mStatus.updateUrl)) return false; try { - mMetrics.startUpdate(); - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mStatus.updateUrl)); // Ensure that the app vs browser disambiguation dialog is not shown. intent.addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER); @@ -203,9 +240,15 @@ public class UpdateStatusProvider { return true; } - private UpdateStatusProvider() { - mOmahaQuery = new UpdateQuery(this::resolveStatus); + protected UpdateStatusProvider(InlineUpdateController inlineController) { + mInlineController = inlineController; + mInlineController.setCallback(this::resolveStatus); + + mOmahaQuery = new UpdateQuery(mInlineController, this::resolveStatus); mMetrics = new UpdateSuccessMetrics(); + + // Note that as a singleton this class never unregisters. + ApplicationStatus.registerStateListenerForAllActivities(this); } @RequiresNonNull("mStatus") @@ -214,35 +257,52 @@ public class UpdateStatusProvider { } private void resolveStatus() { - if (mOmahaQuery.getStatus() != Status.FINISHED) { + if (mOmahaQuery.getStatus() != Status.FINISHED || mInlineController.getStatus() == null) { return; } // We pull the Omaha result once as it will never change. if (mStatus == null) mStatus = new UpdateStatus(assumeNonNull(mOmahaQuery.getResult())); - if (!mStatus.mIsSimulated) { - mStatus.updateState = assumeNonNull(mOmahaQuery.getResult()).updateState; + if (mStatus.mIsSimulated) { // used only during tests + if (mStatus.mIsInlineSimulated) { + @UpdateState + int inlineState = mInlineController.getStatus(); + String updateUrl = mInlineController.getUpdateUrl(); + + if (inlineState == UpdateState.NONE) { + mStatus.updateState = assumeNonNull(mOmahaQuery.getResult()).updateState; + } else { + mStatus.updateState = inlineState; + mStatus.updateUrl = updateUrl; + } + } + } else { + // used by Bromite to resolve update status + // ignores Omaha status + @UpdateState + int inlineState = mInlineController.getStatus(); + mStatus.updateState = inlineState; + mStatus.updateUrl = mInlineController.getUpdateUrl(); } if (!mRecordedInitialStatus) { - mMetrics.analyzeFirstStatus(); mRecordedInitialStatus = true; } pingObservers(); } - private static final class LazyHolder { - private static final UpdateStatusProvider INSTANCE = new UpdateStatusProvider(); - } - private static final class UpdateQuery extends AsyncTask { + static final String TAG = "UpdateStatusProvider"; private final Runnable mCallback; private @Nullable UpdateStatus mStatus; - public UpdateQuery(Runnable resultReceiver) { + private InlineUpdateController mInlineController; + + public UpdateQuery(InlineUpdateController inlineController, Runnable resultReceiver) { + mInlineController = inlineController; mCallback = resultReceiver; } @@ -254,7 +314,7 @@ public class UpdateStatusProvider { protected UpdateStatus doInBackground() { UpdateStatus testStatus = getTestStatus(); if (testStatus != null) return testStatus; - return getRealStatus(); + return getActualStatus(); } @Override @@ -272,6 +332,8 @@ public class UpdateStatusProvider { status.mIsSimulated = true; status.updateState = forcedUpdateState; + status.mIsInlineSimulated = forcedUpdateState == UpdateState.INLINE_UPDATE_AVAILABLE; + // Push custom configurations for certain update states. switch (forcedUpdateState) { case UpdateState.UPDATE_AVAILABLE: @@ -289,31 +351,33 @@ public class UpdateStatusProvider { return status; } - private UpdateStatus getRealStatus() { + private UpdateStatus getActualStatus() { UpdateStatus status = new UpdateStatus(); - if (VersionNumberGetter.isNewerVersionAvailable()) { - status.updateUrl = MarketURLGetter.getMarketUrl(); - status.latestVersion = VersionNumberGetter.getInstance().getLatestKnownVersion(); - - boolean allowedToUpdate = - checkForSufficientStorage() - // Disable the version update check for automotive. See b/297925838. - && !DeviceInfo.isAutomotive() - && PackageUtils.isPackageInstalled( - GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE); - status.updateState = - allowedToUpdate ? UpdateState.UPDATE_AVAILABLE : UpdateState.NONE; - - ChromeSharedPreferences.getInstance() - .removeKey(ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION); - } else if (!VersionNumberGetter.isCurrentOsVersionSupported()) { - status.updateState = UpdateState.UNSUPPORTED_OS_VERSION; - status.latestUnsupportedVersion = - ChromeSharedPreferences.getInstance() - .readString(ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION, null); - } else { - status.updateState = UpdateState.NONE; + SharedPreferences preferences = OmahaPrefUtils.getSharedPreferences(); + status.latestVersion = preferences.getString(OmahaPrefUtils.PREF_LATEST_MODIFIED_VERSION, ""); + + status.updateState = UpdateState.NONE; + if (status.latestVersion != null && status.latestVersion.length() != 0) { + VersionNumber latestVersion = VersionNumber.fromString(status.latestVersion); + if (latestVersion == null) { + Log.e(TAG, "BromiteUpdater: stored latest version '%s' is invalid", status.latestVersion); + } else if (OmahaPrefUtils.isNewVersionAvailableByVersion(latestVersion)) { + status.updateState = UpdateState.INLINE_UPDATE_AVAILABLE; + status.updateUrl = mInlineController.getDownloadUrl(); + return status; + } + String latestUpstreamVersion = preferences.getString(OmahaPrefUtils.PREF_LATEST_UPSTREAM_VERSION, ""); + if (latestUpstreamVersion != null && latestUpstreamVersion.length() != 0) { + VersionNumber upstreamVersion = VersionNumber.fromString(latestUpstreamVersion); + if (upstreamVersion == null) { + Log.e(TAG, "BromiteUpdater: stored latest upstream version '%s' is invalid", latestUpstreamVersion); + } else if (OmahaPrefUtils.isNewVersionAvailableByVersion(upstreamVersion)) { + status.updateUrl = mInlineController.getVulnerableVersionDocUrl(); + status.updateState = UpdateState.VULNERABLE_VERSION; + return status; + } + } } return status; diff --git a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java --- a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java +++ b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java @@ -24,8 +24,8 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; public class VersionNumberGetter { private static VersionNumberGetter sInstance = new VersionNumberGetter(); - /** If true, OmahaClient will never report that a newer version is available. */ - private static boolean sDisableUpdateDetectionForTesting; + /** it must be true to disable version control via Omaha server. */ + private static boolean sDisableUpdateDetectionForTesting = true; @VisibleForTesting static VersionNumberGetter getInstance() { diff --git a/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java new file mode 100644 --- /dev/null +++ b/chrome/browser/omaha/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java @@ -0,0 +1,57 @@ +// 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.omaha.inline; + +import android.app.Activity; +import android.content.Intent; + +import androidx.annotation.Nullable; + +import org.chromium.chrome.browser.omaha.UpdateStatusProvider; + +/** + * Helper for gluing interactions with the Play store's AppUpdateManager with Chrome. This + * involves hooking up to Play as a listener for install state changes, should only happen if we are + * in the foreground. + */ +public interface InlineUpdateController { + void setCallback(Runnable callback); + + /** + * Enables or disables the controller. It will trigger an update check when previously disabled. + * @param enabled true iff the controller should be enabled. + */ + void setEnabled(boolean enabled); + + /** + * @return The current state of the inline update process. May be {@code null} if the state + * hasn't been determined yet. + */ + @Nullable + @UpdateStatusProvider.UpdateState + Integer getStatus(); + + /** + * @return The current update URL for the inline update process. May be an empty string if the state + * hasn't been determined yet or if state does not specify one. + */ + String getUpdateUrl(); + + String getDownloadUrl(); + + String getVulnerableVersionDocUrl(); + + /** + * Starts the update, if possible. This will send an {@link Intent} out to play, which may + * cause Chrome to move to the background. + * @param activity The {@link Activity} to use to interact with Play. + */ + void startUpdate(Activity activity); + + /** + * Completes the Play installation process, if possible. This may cause Chrome to restart. + */ + void completeUpdate(); +} diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchService.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchService.java --- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchService.java +++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchService.java @@ -85,9 +85,6 @@ public class SafetyHubFetchService implements SigninManager.SignInStateObserver, mLocalPasswordsFetchService = new SafetyHubPasswordsFetchService(passwordManagerHelper, prefService, null); - // Fetch latest update status. - UpdateStatusProvider.getInstance().addObserver(mUpdateCallback); - recordMetricForUnusedSitePermissionsSettingState(); } @@ -116,8 +113,6 @@ public class SafetyHubFetchService implements SigninManager.SignInStateObserver, if (mSigninManager != null) { mSigninManager.removeSignInStateObserver(this); } - - UpdateStatusProvider.getInstance().removeObserver(mUpdateCallback); } /** See {@link ChromeActivitySessionTracker#onForegroundSessionStart()}. */ diff --git a/chrome/browser/save_to_drive/drive_uploader.cc b/chrome/browser/save_to_drive/drive_uploader.cc --- a/chrome/browser/save_to_drive/drive_uploader.cc +++ b/chrome/browser/save_to_drive/drive_uploader.cc @@ -290,7 +290,7 @@ DriveUploader::CreateEndpointFetcher( return std::make_unique( /*url_loader_factory=*/url_loader_factory_.get(), /*identity_manager=*/identity_manager_, - /*request_params=*/std::move(request_params)); + /*request_params=*/std::move(request_params), kTrafficAnnotationTag); } DriveUploaderType DriveUploader::get_drive_uploader_type() const { 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 @@ -2444,6 +2444,12 @@ Your Google account may have other forms of browsing history like searches and a Chrome updates are no longer supported for this version of Android + + Allow checking for updates + + + Notify about new releases by periodically checking for their availability + @@ -4489,7 +4495,10 @@ To change this setting, BEGIN_LINKdelete the Chrome d - Update Chrome + Update Bromite + + + Update not available. Read more Newer version is available @@ -4500,6 +4509,18 @@ To change this setting, BEGIN_LINKdelete the Chrome d Android version is unsupported + + Downloading… + + + Couldn’t download + + + Update ready + + + Restart Bromite + New window diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc --- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc @@ -771,7 +771,7 @@ LensOverlayQueryController::CreateEndpointFetcher( .SetTimeout(timeout) .SetUrl(fetch_url) .SetUploadProgressCallback(std::move(upload_progress_callback)) - .Build()); + .Build(), kTrafficAnnotationTag); } void LensOverlayQueryController::SendLatencyGen204IfEnabled( diff --git a/components/commerce/core/account_checker.cc b/components/commerce/core/account_checker.cc --- a/components/commerce/core/account_checker.cc +++ b/components/commerce/core/account_checker.cc @@ -326,7 +326,7 @@ std::unique_ptr AccountChecker::CreateEndpointFetcher( .SetPostData(post_data); MaybeUseAlternateShoppingServer(request_params); return std::make_unique( - url_loader_factory_, identity_manager_, request_params.Build()); + url_loader_factory_, identity_manager_, request_params.Build(), annotation_tag); } } // namespace commerce diff --git a/components/commerce/core/subscriptions/subscriptions_server_proxy.cc b/components/commerce/core/subscriptions/subscriptions_server_proxy.cc --- a/components/commerce/core/subscriptions/subscriptions_server_proxy.cc +++ b/components/commerce/core/subscriptions/subscriptions_server_proxy.cc @@ -302,7 +302,7 @@ SubscriptionsServerProxy::CreateEndpointFetcher( .SetPostData(post_data); MaybeUseAlternateShoppingServer(request_params); return std::make_unique( - url_loader_factory_, identity_manager_, request_params.Build()); + url_loader_factory_, identity_manager_, request_params.Build(), annotation_tag); } void SubscriptionsServerProxy::HandleManageSubscriptionsResponses( diff --git a/components/contextual_search/internal/composebox_query_controller.cc b/components/contextual_search/internal/composebox_query_controller.cc --- a/components/contextual_search/internal/composebox_query_controller.cc +++ b/components/contextual_search/internal/composebox_query_controller.cc @@ -1226,7 +1226,7 @@ ComposeboxQueryController::CreateEndpointFetcher( .SetTimeout(timeout) .SetUploadProgressCallback(std::move(upload_progress_callback)) .SetUrl(fetch_url) - .Build()); + .Build(), kTrafficAnnotationTag); } lens::LensOverlayClientContext ComposeboxQueryController::CreateClientContext() diff --git a/components/data_sharing/internal/data_sharing_network_loader_impl.cc b/components/data_sharing/internal/data_sharing_network_loader_impl.cc --- a/components/data_sharing/internal/data_sharing_network_loader_impl.cc +++ b/components/data_sharing/internal/data_sharing_network_loader_impl.cc @@ -63,7 +63,7 @@ DataSharingNetworkLoaderImpl::CreateEndpointFetcher( .SetUrl(url) .SetOAuthConsumerId(signin::OAuthConsumerId::kDataSharingAndroid) .SetPostData(post_data) - .Build()); + .Build(), annotation_tag); } void DataSharingNetworkLoaderImpl::OnDownloadComplete( diff --git a/components/data_sharing/internal/preview_server_proxy.cc b/components/data_sharing/internal/preview_server_proxy.cc --- a/components/data_sharing/internal/preview_server_proxy.cc +++ b/components/data_sharing/internal/preview_server_proxy.cc @@ -345,7 +345,7 @@ std::unique_ptr PreviewServerProxy::CreateEndpointFetcher( .SetContentType(kContentType) .SetTimeout(kTimeout) .SetUrl(url) - .Build()); + .Build(), kGetSharedDataPreviewTrafficAnnotation); } void PreviewServerProxy::HandleServerResponse( diff --git a/components/endpoint_fetcher/endpoint_fetcher.cc b/components/endpoint_fetcher/endpoint_fetcher.cc --- a/components/endpoint_fetcher/endpoint_fetcher.cc +++ b/components/endpoint_fetcher/endpoint_fetcher.cc @@ -29,6 +29,11 @@ #include "services/network/public/mojom/referrer_policy.mojom-shared.h" #include "services/network/public/mojom/url_response_head.mojom.h" +// used for the Bromite customization +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" +#include "services/network/public/cpp/resource_request.h" + namespace endpoint_fetcher { namespace { @@ -47,6 +52,8 @@ std::string GetHttpMethodString(const HttpMethod& http_method) { return "DELETE"; case HttpMethod::kPut: return "PUT"; + case HttpMethod::kHead: + return "HEAD"; default: DCHECK(0) << base::StringPrintf("Unknown HttpMethod %d\n", static_cast(http_method)); @@ -54,6 +61,13 @@ std::string GetHttpMethodString(const HttpMethod& http_method) { return ""; } +HttpMethod GetHttpMethod(const std::string& http_method_string) { + if (http_method_string == "HEAD") { + return HttpMethod::kHead; + } + return HttpMethod::kUndefined; +} + } // namespace EndpointResponse::EndpointResponse() = default; @@ -109,9 +123,11 @@ EndpointFetcher::RequestParams::Builder::Build() { EndpointFetcher::EndpointFetcher( const scoped_refptr& url_loader_factory, signin::IdentityManager* identity_manager, - RequestParams request_params) + RequestParams request_params, + const net::NetworkTrafficAnnotationTag& annotation_tag) : url_loader_factory_(url_loader_factory), identity_manager_(identity_manager), + intercept_redirect_(false), request_params_(std::move(request_params)) { if (request_params_.auth_type() == OAUTH) { DCHECK(identity_manager_) @@ -124,12 +140,32 @@ EndpointFetcher::EndpointFetcher( EndpointFetcher::EndpointFetcher( const net::NetworkTrafficAnnotationTag& annotation_tag) : identity_manager_(nullptr), + intercept_redirect_(false), request_params_( EndpointFetcher::RequestParams::Builder(HttpMethod::kUndefined, annotation_tag) .SetTimeout(kDefaultTimeOut) .Build()) {} +// constructor used by Cromite +EndpointFetcher::EndpointFetcher( + const scoped_refptr& url_loader_factory, + const GURL& url, + const std::string& http_method, + int64_t timeout_ms, + const bool intercept_redirect, + const net::NetworkTrafficAnnotationTag& annotation_tag) + : url_loader_factory_(url_loader_factory), + identity_manager_(nullptr), + intercept_redirect_(intercept_redirect), + request_params_( + EndpointFetcher::RequestParams::Builder(GetHttpMethod(http_method), + annotation_tag) + .SetAuthType(NO_AUTH) + .SetTimeout(base::Milliseconds(timeout_ms)) + .SetUrl(url) + .Build()) {} + EndpointFetcher::~EndpointFetcher() = default; void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) { @@ -212,13 +248,10 @@ void EndpointFetcher::PerformHttpRequest( auto resource_request = std::make_unique(); resource_request->method = GetHttpMethodString(request_params_.http_method()); resource_request->url = request_params_.url(); - resource_request->credentials_mode = GetCredentialsMode(); - - if (GetSetSiteForCookies()) { - resource_request->site_for_cookies = - net::SiteForCookies::FromUrl(request_params_.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; // Add Content-Type header if post data is present. bool has_body_content = (request_params_.http_method() == HttpMethod::kPost || request_params_.http_method() == HttpMethod::kPut) && @@ -259,8 +292,23 @@ void EndpointFetcher::PerformHttpRequest( break; } + if (intercept_redirect_ == true) { + // will need manual mode to capture the landing page URL + resource_request->redirect_mode = network::mojom::RedirectMode::kManual; // default is kFollow + } + simple_url_loader_ = network::SimpleURLLoader::Create( std::move(resource_request), request_params_.annotation_tag()); + simple_url_loader_->SetAllowHttpErrorResults(true); + + if (!response_) + response_ = std::make_unique(); + + if (intercept_redirect_ == true) { + // use a callback to capture landing page URL + simple_url_loader_->SetOnRedirectCallback(base::BindRepeating( + &EndpointFetcher::OnSimpleLoaderRedirect, base::Unretained(this))); + } if (has_body_content) { simple_url_loader_->AttachStringForUpload( @@ -273,9 +321,18 @@ void EndpointFetcher::PerformHttpRequest( simple_url_loader_->SetRetryOptions(GetMaxRetries(), network::SimpleURLLoader::RETRY_ON_5XX); - simple_url_loader_->SetTimeoutDuration(request_params_.timeout()); - simple_url_loader_->SetAllowHttpErrorResults(true); - + LOG(INFO) << "performing " + << GetHttpMethodString(request_params_.http_method()) + << " request to " << url_; + if (request_params_.http_method() == HttpMethod::kHead) { + endpoint_fetcher_callback_ = std::move(endpoint_fetcher_callback); + + simple_url_loader_->DownloadHeadersOnly( + url_loader_factory_.get(), + base::BindOnce(&EndpointFetcher::OnURLLoadComplete, + base::Unretained(this))); + return; + } network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback = base::BindOnce(&EndpointFetcher::OnResponseFetched, weak_ptr_factory_.GetWeakPtr(), @@ -334,21 +391,6 @@ void EndpointFetcher::OnResponseFetched( std::move(endpoint_fetcher_callback).Run(std::move(response)); } -network::mojom::CredentialsMode EndpointFetcher::GetCredentialsMode() const { - if (!request_params_.credentials_mode.has_value()) { - return network::mojom::CredentialsMode::kOmit; - } - switch (request_params_.credentials_mode.value()) { - case CredentialsMode::kOmit: - return network::mojom::CredentialsMode::kOmit; - case CredentialsMode::kInclude: - return network::mojom::CredentialsMode::kInclude; - } - DCHECK(0) << base::StringPrintf( - "Credentials mode %d not currently supported by EndpointFetcher\n", - static_cast(request_params_.credentials_mode.value())); -} - int EndpointFetcher::GetMaxRetries() const { return request_params_.max_retries.value_or(kNumRetries); } @@ -366,4 +408,39 @@ std::string EndpointFetcher::GetUrlForTesting() { return request_params_.url().spec(); } +void EndpointFetcher::OnSimpleLoaderRedirect( + const GURL& url_before_redirect, + const net::RedirectInfo& redirect_info, + const network::mojom::URLResponseHead& response_head, + std::vector* removed_headers) { + url_ = redirect_info.new_url; + if (response_ && response_->redirect_url.empty()) { + response_->redirect_url = url_.spec(); + response_->response = std::to_string(redirect_info.status_code); + } else { + LOG(INFO) << "BromiteUpdater: redirect URL is not empty, status code is " << redirect_info.status_code; + } + + std::move(endpoint_fetcher_callback_).Run(std::move(response_)); +} + +void EndpointFetcher::OnURLLoadComplete( + scoped_refptr 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_)); +} + } // namespace endpoint_fetcher diff --git a/components/endpoint_fetcher/endpoint_fetcher.h b/components/endpoint_fetcher/endpoint_fetcher.h --- a/components/endpoint_fetcher/endpoint_fetcher.h +++ b/components/endpoint_fetcher/endpoint_fetcher.h @@ -19,6 +19,9 @@ #include "services/network/public/mojom/fetch_api.mojom-forward.h" #include "url/gurl.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_response_head.mojom.h" + namespace base { class TimeDelta; } // namespace base @@ -60,6 +63,7 @@ enum class HttpMethod { kPost = 1, kDelete = 2, kPut = 3, + kHead = 4, }; enum AuthType { @@ -78,6 +82,8 @@ struct EndpointResponse { ~EndpointResponse(); std::string response; + long last_modified; + std::string redirect_url; int http_status_code{-1}; std::optional error_type; scoped_refptr headers; @@ -300,7 +306,16 @@ class EndpointFetcher { EndpointFetcher( const scoped_refptr& url_loader_factory, signin::IdentityManager* identity_manager, - RequestParams request_params); + RequestParams request_params, + const net::NetworkTrafficAnnotationTag& annotation_tag); + + // Constructor used by cromite + EndpointFetcher(const scoped_refptr& url_loader_factory, + const GURL& url, + const std::string& http_method, + int64_t timeout_ms, + const bool intercept_redirect, + const net::NetworkTrafficAnnotationTag& annotation_tag); EndpointFetcher(const EndpointFetcher& endpoint_fetcher) = delete; EndpointFetcher& operator=(const EndpointFetcher& endpoint_fetcher) = delete; @@ -333,11 +348,17 @@ class EndpointFetcher { void OnResponseFetched(EndpointFetcherCallback callback, std::optional response_body); + void OnURLLoadComplete(scoped_refptr headers); + void OnSimpleLoaderRedirect(const GURL& url_before_redirect, + const net::RedirectInfo& redirect_info, + const network::mojom::URLResponseHead& response_head, + std::vector* removed_headers); network::mojom::CredentialsMode GetCredentialsMode() const; int GetMaxRetries() const; bool GetSetSiteForCookies() const; UploadProgressCallback GetUploadProgressCallback() const; + GURL url_; // Members set in constructor const scoped_refptr url_loader_factory_; @@ -348,6 +369,8 @@ class EndpointFetcher { // SingleClientSharedTabGroupVersioningSyncTest.ShouldShowVersioningMessagesAfterRestart/kSyncTransportOnly const raw_ptr identity_manager_; + const bool intercept_redirect_; + // The complete definition of the specific network request to be performed. // Contains authentication details and response handling preferences. const RequestParams request_params_; @@ -357,6 +380,9 @@ class EndpointFetcher { access_token_fetcher_; std::unique_ptr simple_url_loader_; + EndpointFetcherCallback endpoint_fetcher_callback_; + std::unique_ptr response_; + base::WeakPtrFactory weak_ptr_factory_{this}; }; diff --git a/components/manta/base_provider.cc b/components/manta/base_provider.cc --- a/components/manta/base_provider.cc +++ b/components/manta/base_provider.cc @@ -140,7 +140,7 @@ std::unique_ptr BaseProvider::CreateEndpointFetcher( .SetUrl(url) .SetOAuthConsumerId(signin::OAuthConsumerId::kManta) .SetPostData(post_data) - .Build()); + .Build(), annotation_tag); } std::unique_ptr BaseProvider::CreateEndpointFetcherForDemoMode( @@ -158,7 +158,7 @@ std::unique_ptr BaseProvider::CreateEndpointFetcherForDemoMode( .SetTimeout(timeout) .SetPostData(post_data) .SetChannel(version_info::Channel::STABLE) - .Build()); + .Build(), annotation_tag); } } // namespace manta diff --git a/cromite_flags/chrome/browser/flags/android/chrome_feature_list_cc/Bromite-auto-updater.inc b/cromite_flags/chrome/browser/flags/android/chrome_feature_list_cc/Bromite-auto-updater.inc new file mode 100644 --- /dev/null +++ b/cromite_flags/chrome/browser/flags/android/chrome_feature_list_cc/Bromite-auto-updater.inc @@ -0,0 +1,3 @@ +CROMITE_FEATURE(kInlineUpdateFlow, + "InlineUpdateFlow", + base::FEATURE_ENABLED_BY_DEFAULT); diff --git a/cromite_flags/chrome/browser/flags/android/chrome_feature_list_h/Bromite-auto-updater.inc b/cromite_flags/chrome/browser/flags/android/chrome_feature_list_h/Bromite-auto-updater.inc new file mode 100644 --- /dev/null +++ b/cromite_flags/chrome/browser/flags/android/chrome_feature_list_h/Bromite-auto-updater.inc @@ -0,0 +1 @@ +BASE_DECLARE_FEATURE(kInlineUpdateFlow); --