0

Reland: Display message when Android-OS advanced-protection-mode changes

This CL displays message when Android-OS provided
advanced-protection-mode changes:
- On startup if state changed while Chrome was closed
- When the setting changed if Chrome is running

The CL was reverted because the CL attempted to do a native call
- PermissionsAndroidFeatureMap#isEnabled() prior to native being
ready.

BUG=401529207
TEST=AdvancedProtectionMediatorTest

Change-Id: Ia89aa50513d335e96f71807423d68fef660d9469
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6386729
Reviewed-by: Tomasz Wiszkowski <ender@google.com>
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: Thomas Nguyen <tungnh@chromium.org>
Reviewed-by: Javier Castro <jacastro@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Xinghui Lu <xinghuilu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1437804}
This commit is contained in:
Peter Kotwicz
2025-03-25 16:02:46 -07:00
committed by Chromium LUCI CQ
parent 0441655922
commit 7c00607962
12 changed files with 464 additions and 3 deletions
chrome
android
BUILD.gn
java
src
org
chromium
chrome
browser
preferences
android
java
src
org
chromium
chrome
browser
safe_browsing
components
messages
android
java
src
org
chromium
components
message_enums.h
permissions
android
java
src
org
chromium
tools/metrics/histograms/metadata/android

@ -1086,6 +1086,7 @@ if (current_toolchain == default_toolchain) {
"//chrome/browser/readaloud/android:junit", "//chrome/browser/readaloud/android:junit",
"//chrome/browser/recent_tabs:junit", "//chrome/browser/recent_tabs:junit",
"//chrome/browser/recent_tabs/internal:junit", "//chrome/browser/recent_tabs/internal:junit",
"//chrome/browser/safe_browsing/android:junit",
"//chrome/browser/safety_check/android:junit", "//chrome/browser/safety_check/android:junit",
"//chrome/browser/safety_hub/android:junit", "//chrome/browser/safety_hub/android:junit",
"//chrome/browser/search_engines/android:junit", "//chrome/browser/search_engines/android:junit",

@ -116,6 +116,7 @@ import org.chromium.chrome.browser.privacy_sandbox.SurfaceType;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.read_later.ReadLaterIphController; import org.chromium.chrome.browser.read_later.ReadLaterIphController;
import org.chromium.chrome.browser.readaloud.ReadAloudIphController; import org.chromium.chrome.browser.readaloud.ReadAloudIphController;
import org.chromium.chrome.browser.safe_browsing.AdvancedProtectionCoordinator;
import org.chromium.chrome.browser.search_engines.choice_screen.ChoiceDialogCoordinator; import org.chromium.chrome.browser.search_engines.choice_screen.ChoiceDialogCoordinator;
import org.chromium.chrome.browser.share.ShareDelegate; import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.share.link_to_text.LinkToTextIphController; import org.chromium.chrome.browser.share.link_to_text.LinkToTextIphController;
@ -249,6 +250,7 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
private @Nullable LoadingFullscreenCoordinator mLoadingFullscreenCoordinator; private @Nullable LoadingFullscreenCoordinator mLoadingFullscreenCoordinator;
private @Nullable BookmarkOpener mBookmarkOpener; private @Nullable BookmarkOpener mBookmarkOpener;
private @NonNull ObservableSupplier<BookmarkManagerOpener> mBookmarkManagerOpenerSupplier; private @NonNull ObservableSupplier<BookmarkManagerOpener> mBookmarkManagerOpenerSupplier;
private @NonNull AdvancedProtectionCoordinator mAdvancedProtectionCoordinator;
// Activity tab observer that updates the current tab used by various UI components. // Activity tab observer that updates the current tab used by various UI components.
private class RootUiTabObserver extends ActivityTabTabObserver { private class RootUiTabObserver extends ActivityTabTabObserver {
@ -625,6 +627,11 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
mLoadingFullscreenCoordinator = null; mLoadingFullscreenCoordinator = null;
} }
if (mAdvancedProtectionCoordinator != null) {
mAdvancedProtectionCoordinator.destroy();
mAdvancedProtectionCoordinator = null;
}
super.onDestroy(); super.onDestroy();
} }
@ -739,6 +746,8 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
super.onFinishNativeInitialization(); super.onFinishNativeInitialization();
assert mLayoutManager != null; assert mLayoutManager != null;
mAdvancedProtectionCoordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
UmaSessionStats.registerSyntheticFieldTrial( UmaSessionStats.registerSyntheticFieldTrial(
"AndroidNavigationMode", "AndroidNavigationMode",
UiUtils.isGestureNavigationMode(mActivity.getWindow()) UiUtils.isGestureNavigationMode(mActivity.getWindow())
@ -1541,6 +1550,10 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
return true; return true;
} }
if (mAdvancedProtectionCoordinator.showMessageOnStartupIfNeeded()) {
return true;
}
return triggerPromo(profile, intentWithEffect); return triggerPromo(profile, intentWithEffect);
} }

@ -252,8 +252,16 @@ public final class ChromePreferenceKeys {
"Chrome.RequestDesktopSiteGlobalSetting.DefaultEnabled"; "Chrome.RequestDesktopSiteGlobalSetting.DefaultEnabled";
/** /**
* Indicates that Chrome should show an alert to the user about data privacy if the device * Indicates the state of the Android-OS-provided advanced-protection setting when Chrome was
* lock is removed. * last opened. Used to determine whether to show on startup a message informing the user about
* the setting change.
*/
public static final String DEFAULT_OS_ADVANCED_PROTECTION_SETTING =
"Chrome.OsAdvancedProtection.DefaultEnabled";
/**
* Indicates that Chrome should show an alert to the user about data privacy if the device lock
* is removed.
*/ */
public static final String DEVICE_LOCK_SHOW_ALERT_IF_REMOVED = public static final String DEVICE_LOCK_SHOW_ALERT_IF_REMOVED =
"Chrome.DeviceLock.ShowAlertIfRemoved"; "Chrome.DeviceLock.ShowAlertIfRemoved";
@ -1005,6 +1013,7 @@ public final class ChromePreferenceKeys {
DEFAULT_BROWSER_PROMO_PROMOED_COUNT, DEFAULT_BROWSER_PROMO_PROMOED_COUNT,
DEFAULT_BROWSER_PROMO_SESSION_COUNT, DEFAULT_BROWSER_PROMO_SESSION_COUNT,
DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING, DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING,
DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
DEPRECATED_HOMEPAGE_LOCATION_POLICY, DEPRECATED_HOMEPAGE_LOCATION_POLICY,
DEPRECATED_HOMEPAGE_PARTNER_CUSTOMIZED_DEFAULT_URI, DEPRECATED_HOMEPAGE_PARTNER_CUSTOMIZED_DEFAULT_URI,
DEVICE_LOCK_SHOW_ALERT_IF_REMOVED, DEVICE_LOCK_SHOW_ALERT_IF_REMOVED,

@ -44,6 +44,8 @@ source_set("android") {
android_library("java") { android_library("java") {
sources = [ sources = [
"java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java",
"java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingBridge.java", "java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingBridge.java",
"java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java", "java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java",
"java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java", "java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java",
@ -55,6 +57,7 @@ android_library("java") {
deps = [ deps = [
":java_resources", ":java_resources",
"//base:base_java", "//base:base_java",
"//base:supplier_java",
"//build/android:build_java", "//build/android:build_java",
"//chrome/browser/feedback/android:java", "//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java", "//chrome/browser/flags:java",
@ -65,6 +68,9 @@ android_library("java") {
"//components/browser_ui/settings/android:java", "//components/browser_ui/settings/android:java",
"//components/browser_ui/util/android:java", "//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java", "//components/browser_ui/widget/android:java",
"//components/messages/android:java",
"//components/permissions/android:core_java",
"//components/permissions/android:java",
"//components/prefs/android:java", "//components/prefs/android:java",
"//components/user_prefs/android:java", "//components/user_prefs/android:java",
"//content/public/android:content_java", "//content/public/android:content_java",
@ -129,6 +135,28 @@ android_library("javatests") {
] ]
} }
robolectric_library("junit") {
sources = [ "java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java" ]
deps = [
":java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base:base_shared_preferences_java",
"//base:service_loader_java",
"//base:unowned_user_data_java",
"//chrome/browser/preferences:java",
"//components/messages/android:factory_java",
"//components/messages/android:java",
"//components/messages/android:manager_java",
"//components/permissions/android:core_java",
"//components/permissions/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java",
]
}
android_resources("java_resources") { android_resources("java_resources") {
sources = [ sources = [
"java/res/layout/radio_button_group_safe_browsing_preference.xml", "java/res/layout/radio_button_group_safe_browsing_preference.xml",

@ -0,0 +1,30 @@
// Copyright 2025 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.safe_browsing;
import org.chromium.ui.base.WindowAndroid;
/** A class for showing UI whenever the Android-OS-supplied advanced-protection state changes. */
public class AdvancedProtectionCoordinator {
private AdvancedProtectionMediator mMediator;
public AdvancedProtectionCoordinator(WindowAndroid windowAndroid) {
mMediator = new AdvancedProtectionMediator(windowAndroid);
}
public void destroy() {
mMediator.destroy();
}
/**
* Shows message-UI informing the user about the Android-OS requested advanced-protection state
* if the advanced-protection state has changed since Chrome was last open.
*
* @return whether message-UI was shown.
*/
public boolean showMessageOnStartupIfNeeded() {
return mMediator.showMessageOnStartupIfNeeded();
}
}

@ -0,0 +1,93 @@
// Copyright 2025 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.safe_browsing;
import static org.chromium.build.NullUtil.assumeNonNull;
import androidx.annotation.NonNull;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.components.messages.MessageDispatcherProvider;
import org.chromium.components.permissions.OsAdditionalSecurityPermissionProvider;
import org.chromium.components.permissions.OsAdditionalSecurityPermissionUtil;
import org.chromium.components.permissions.PermissionsAndroidFeatureList;
import org.chromium.components.permissions.PermissionsAndroidFeatureMap;
import org.chromium.ui.base.WindowAndroid;
/** A class for showing UI whenever the Android-OS-supplied advanced-protection state changes. */
public class AdvancedProtectionMediator implements OsAdditionalSecurityPermissionProvider.Observer {
private WindowAndroid mWindowAndroid;
public AdvancedProtectionMediator(WindowAndroid windowAndroid) {
mWindowAndroid = windowAndroid;
var provider = OsAdditionalSecurityPermissionUtil.getProviderInstance();
if (provider != null
&& !PermissionsAndroidFeatureMap.isEnabled(
PermissionsAndroidFeatureList
.OS_ADDITIONAL_SECURITY_PERMISSION_KILL_SWITCH)) {
provider.addObserver(this);
}
}
public void destroy() {
var provider = OsAdditionalSecurityPermissionUtil.getProviderInstance();
if (provider != null) {
provider.removeObserver(this);
}
}
public boolean showMessageOnStartupIfNeeded() {
if (PermissionsAndroidFeatureMap.isEnabled(
PermissionsAndroidFeatureList.OS_ADDITIONAL_SECURITY_PERMISSION_KILL_SWITCH)) {
return false;
}
var provider = OsAdditionalSecurityPermissionUtil.getProviderInstance();
if (provider == null) return false;
boolean cachedAdvancedProtectionSetting =
ChromeSharedPreferences.getInstance()
.readBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
/* defaultValue= */ false);
if (cachedAdvancedProtectionSetting == provider.isAdvancedProtectionRequestedByOs()) {
return false;
}
updatePref(provider);
enqueueMessage(provider);
return true;
}
@Override
public void onAdvancedProtectionOsSettingChanged() {
var provider = OsAdditionalSecurityPermissionUtil.getProviderInstance();
if (provider == null) return;
updatePref(provider);
enqueueMessage(provider);
}
private void enqueueMessage(@NonNull OsAdditionalSecurityPermissionProvider provider) {
var context = assumeNonNull(mWindowAndroid.getContext()).get();
var propertyModel =
provider.buildAdvancedProtectionMessagePropertyModel(
context, /* primaryButtonAction= */ null);
if (propertyModel == null) {
return;
}
MessageDispatcherProvider.from(mWindowAndroid)
.enqueueWindowScopedMessage(propertyModel, /* highPriority= */ false);
}
private void updatePref(@NonNull OsAdditionalSecurityPermissionProvider provider) {
ChromeSharedPreferences.getInstance()
.writeBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
provider.isAdvancedProtectionRequestedByOs());
}
}

@ -0,0 +1,249 @@
// Copyright 2025 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.safe_browsing;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.ContextUtils;
import org.chromium.base.ServiceLoaderUtil;
import org.chromium.base.ThreadUtils;
import org.chromium.base.UnownedUserDataHost;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.components.messages.ManagedMessageDispatcher;
import org.chromium.components.messages.MessagesFactory;
import org.chromium.components.permissions.OsAdditionalSecurityPermissionProvider;
import org.chromium.components.permissions.OsAdditionalSecurityPermissionUtil;
import org.chromium.components.permissions.PermissionsAndroidFeatureList;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modelutil.PropertyModel;
import java.lang.ref.WeakReference;
/** Tests for {@link AdvancedProtectionMediator}. */
@RunWith(BaseRobolectricTestRunner.class)
@DisableFeatures(PermissionsAndroidFeatureList.OS_ADDITIONAL_SECURITY_PERMISSION_KILL_SWITCH)
@Config(manifest = Config.NONE)
public class AdvancedProtectionMediatorTest {
@Mock private WindowAndroid mWindowAndroid;
@Mock private Context mContext;
private WeakReference<Context> mWeakContext = new WeakReference<Context>(mContext);
private final UnownedUserDataHost mWindowUserDataHost = new UnownedUserDataHost();
@Mock private ManagedMessageDispatcher mMessageDispatcher;
private static class TestPermissionProvider extends OsAdditionalSecurityPermissionProvider {
private boolean mIsAdvancedProtectionRequestedByOs;
private Observer mObserver;
public TestPermissionProvider(boolean isAdvancedProtectionRequestedByOs) {
mIsAdvancedProtectionRequestedByOs = isAdvancedProtectionRequestedByOs;
}
@Override
public void addObserver(Observer observer) {
assert mObserver == null;
mObserver = observer;
}
@Override
public boolean isAdvancedProtectionRequestedByOs() {
return mIsAdvancedProtectionRequestedByOs;
}
@Override
public @Nullable PropertyModel buildAdvancedProtectionMessagePropertyModel(
Context context, Runnable primaryButtonAction) {
return new PropertyModel();
}
public void setAdvancedProtectionRequestedByOs(boolean isAdvancedProtectionRequestedByOs) {
mIsAdvancedProtectionRequestedByOs = isAdvancedProtectionRequestedByOs;
if (mObserver != null) {
mObserver.onAdvancedProtectionOsSettingChanged();
}
}
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mWindowAndroid.getUnownedUserDataHost()).thenReturn(mWindowUserDataHost);
when(mWindowAndroid.getContext()).thenReturn(mWeakContext);
MessagesFactory.attachMessageDispatcher(mWindowAndroid, mMessageDispatcher);
ContextUtils.getAppSharedPreferences().edit().clear();
OsAdditionalSecurityPermissionUtil.resetForTesting();
}
private TestPermissionProvider setPermissionProvider(
boolean isAdvancedProtectionRequestedByOs) {
var provider = new TestPermissionProvider(isAdvancedProtectionRequestedByOs);
ThreadUtils.runOnUiThreadBlocking(
() -> {
ServiceLoaderUtil.setInstanceForTesting(
OsAdditionalSecurityPermissionProvider.class, provider);
});
return provider;
}
private void verifyEnqueuedMessage() {
verify(mMessageDispatcher, times(1)).enqueueWindowScopedMessage(any(), anyBoolean());
}
private void verifyDidNotEnqueueMessage() {
verify(mMessageDispatcher, times(0)).enqueueWindowScopedMessage(any(), anyBoolean());
}
/**
* Test that {@link AdvancedProtectionCoordinator#showMessageOnStartupIfNeeded()} does not show
* a message if the pref is not stored and advanced-protection-mode is off.
*/
@Test
public void testDontShowMessageNoPrefAdvancedProtectionOff() {
setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ false);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyDidNotEnqueueMessage();
coordinator.destroy();
}
/**
* Test that {@link AdvancedProtectionCoordinator#showMessageOnStartupIfNeeded()} shows a
* message if the pref is not stored and advanced-protection-mode is on.
*/
@Test
public void testShowMessageNoPrefAdvancedProtectionOn() {
setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ true);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyEnqueuedMessage();
coordinator.destroy();
}
/**
* Test that {@link AdvancedProtectionCoordinator#showMessageOnStartupIfNeeded()} does not show
* a message if a pref is stored and its value matches the current advanced-protection-mode
* state.
*/
@Test
public void testDontShowMessagePrefMatches() {
ChromeSharedPreferences.getInstance()
.writeBoolean(ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING, true);
setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ true);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyDidNotEnqueueMessage();
coordinator.destroy();
}
/**
* Test that {@link AdvancedProtectionCoordinator#showMessageOnStartupIfNeeded()} shows a
* message if a pref is stored and its value is true and advanced-protection-mode is off.
*/
@Test
public void testShowMessagePrefTrueAndDiffers() {
var sharedPreferences = ChromeSharedPreferences.getInstance();
sharedPreferences.writeBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING, true);
setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ false);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyEnqueuedMessage();
assertFalse(
sharedPreferences.readBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
/* defaultValue= */ false));
coordinator.destroy();
}
/**
* Test that {@link AdvancedProtectionCoordinator#showMessageOnStartupIfNeeded()} shows a
* message if a pref is stored and its value is false and advanced-protection-mode is on.
*/
@Test
public void testShowMessagePrefFalseAndDiffers() {
var sharedPreferences = ChromeSharedPreferences.getInstance();
sharedPreferences.writeBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING, false);
setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ true);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyEnqueuedMessage();
assertTrue(
sharedPreferences.readBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
/* defaultValue= */ false));
coordinator.destroy();
}
/**
* Test that message is shown when advanced-protection-state is changed while Chrome is running.
*/
@Test
public void testShowMessageOnStateChange() {
var sharedPreferences = ChromeSharedPreferences.getInstance();
sharedPreferences.writeBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING, true);
var provider = setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ true);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyDidNotEnqueueMessage();
provider.setAdvancedProtectionRequestedByOs(/* isAdvancedProtectionRequestedByOs= */ false);
verifyEnqueuedMessage();
coordinator.destroy();
}
/** Test that a message is not shown when the feature-kill-switch is set. */
@Test
@EnableFeatures({PermissionsAndroidFeatureList.OS_ADDITIONAL_SECURITY_PERMISSION_KILL_SWITCH})
public void testDontShowMessageKillSwitch() {
var sharedPreferences = ChromeSharedPreferences.getInstance();
sharedPreferences.writeBoolean(
ChromePreferenceKeys.DEFAULT_OS_ADVANCED_PROTECTION_SETTING, true);
var provider = setPermissionProvider(/* isAdvancedProtectionRequestedByOs= */ false);
var coordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
coordinator.showMessageOnStartupIfNeeded();
verifyDidNotEnqueueMessage();
provider.setAdvancedProtectionRequestedByOs(/* isAdvancedProtectionRequestedByOs= */ true);
verifyDidNotEnqueueMessage();
coordinator.destroy();
}
}

@ -373,6 +373,8 @@ public class MessagesMetrics {
return "CctAccountMismatchNotice"; return "CctAccountMismatchNotice";
case MessageIdentifier.PROMPT_HATS_CLEAR_BROWSING_DATA: case MessageIdentifier.PROMPT_HATS_CLEAR_BROWSING_DATA:
return "PromptHatsClearBrowsingData"; return "PromptHatsClearBrowsingData";
case MessageIdentifier.OS_ADVANCED_PROTECTION_SETTING_CHANGED_MESSAGE:
return "OsAdvancedProtectionSettingChangedMessage";
default: default:
return "Unknown"; return "Unknown";
} }

@ -142,6 +142,7 @@ enum class MessageIdentifier {
COLLABORATION_REMOVED = 57, COLLABORATION_REMOVED = 57,
CCT_ACCOUNT_MISMATCH_NOTICE = 58, CCT_ACCOUNT_MISMATCH_NOTICE = 58,
PROMPT_HATS_CLEAR_BROWSING_DATA = 59, PROMPT_HATS_CLEAR_BROWSING_DATA = 59,
OS_ADVANCED_PROTECTION_SETTING_CHANGED_MESSAGE = 60,
// Insert new values before this line. // Insert new values before this line.
COUNT COUNT
}; };

@ -6,7 +6,10 @@ package org.chromium.components.permissions;
import android.content.Context; import android.content.Context;
import androidx.annotation.Nullable;
import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.NullMarked;
import org.chromium.ui.modelutil.PropertyModel;
/** /**
* Placeholder provider class to query whether the operating system has granted various security * Placeholder provider class to query whether the operating system has granted various security
@ -14,11 +17,30 @@ import org.chromium.build.annotations.NullMarked;
*/ */
@NullMarked @NullMarked
public abstract class OsAdditionalSecurityPermissionProvider { public abstract class OsAdditionalSecurityPermissionProvider {
public interface Observer {
/** Called when the Android-OS advanced-protection-mode setting changes. */
void onAdvancedProtectionOsSettingChanged();
}
public void addObserver(Observer observer) {}
public void removeObserver(Observer observer) {}
/**
* Returns whether the Android OS requests advanced-protection-mode. Implementations must allow
* querying from any thread.
*/
public boolean isAdvancedProtectionRequestedByOs() {
return !hasJavascriptOptimizerPermission();
}
/** /**
* Returns whether the operating system has granted permission to enable javascript optimizers. * Returns whether the operating system has granted permission to enable javascript optimizers.
* Implementations must allow querying from any thread. * Implementations must allow querying from any thread.
*/ */
public abstract boolean hasJavascriptOptimizerPermission(); public boolean hasJavascriptOptimizerPermission() {
return false;
}
/** /**
* Returns message to display in site settings explaining why the operating system has denied * Returns message to display in site settings explaining why the operating system has denied
@ -27,4 +49,15 @@ public abstract class OsAdditionalSecurityPermissionProvider {
public String getJavascriptOptimizerMessage(Context context) { public String getJavascriptOptimizerMessage(Context context) {
return ""; return "";
} }
/**
* Returns {@link PropertyModel} for message-UI to notify user about new
* advanced-protection-mode state.
*
* @param primaryButtonAction The action to run when the message-UI primary-button is clicked.
*/
public @Nullable PropertyModel buildAdvancedProtectionMessagePropertyModel(
Context context, Runnable primaryButtonAction) {
return null;
}
} }

@ -1154,6 +1154,7 @@ chromium-metrics-reviews@google.com.
<int value="57" label="CollaborationRemoved"/> <int value="57" label="CollaborationRemoved"/>
<int value="58" label="CctAccountMismatchNotice"/> <int value="58" label="CctAccountMismatchNotice"/>
<int value="59" label="PromptHatsClearBrowsingData"/> <int value="59" label="PromptHatsClearBrowsingData"/>
<int value="60" label="OsAdvancedProtectionSettingChangedMessage"/>
</enum> </enum>
<!-- LINT.ThenChange(//components/messages/android/message_enums.h:MessageIdentifier) --> <!-- LINT.ThenChange(//components/messages/android/message_enums.h:MessageIdentifier) -->

@ -177,6 +177,7 @@ chromium-metrics-reviews@google.com.
<variant name=".NearOomReduction"/> <variant name=".NearOomReduction"/>
<variant name=".NotificationBlocked"/> <variant name=".NotificationBlocked"/>
<variant name=".OfferNotification"/> <variant name=".OfferNotification"/>
<variant name=".OsAdvancedProtectionSettingChangedMessage"/>
<variant name=".PermissionBlocked"/> <variant name=".PermissionBlocked"/>
<variant name=".PermissionUpdate"/> <variant name=".PermissionUpdate"/>
<variant name=".PopupBlocked"/> <variant name=".PopupBlocked"/>