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}
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index cb6c7104..575ce76 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1086,6 +1086,7 @@
       "//chrome/browser/readaloud/android:junit",
       "//chrome/browser/recent_tabs:junit",
       "//chrome/browser/recent_tabs/internal:junit",
+      "//chrome/browser/safe_browsing/android:junit",
       "//chrome/browser/safety_check/android:junit",
       "//chrome/browser/safety_hub/android:junit",
       "//chrome/browser/search_engines/android:junit",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index ace633f..49063637 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -116,6 +116,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.read_later.ReadLaterIphController;
 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.share.ShareDelegate;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextIphController;
@@ -249,6 +250,7 @@
     private @Nullable LoadingFullscreenCoordinator mLoadingFullscreenCoordinator;
     private @Nullable BookmarkOpener mBookmarkOpener;
     private @NonNull ObservableSupplier<BookmarkManagerOpener> mBookmarkManagerOpenerSupplier;
+    private @NonNull AdvancedProtectionCoordinator mAdvancedProtectionCoordinator;
 
     // Activity tab observer that updates the current tab used by various UI components.
     private class RootUiTabObserver extends ActivityTabTabObserver {
@@ -625,6 +627,11 @@
             mLoadingFullscreenCoordinator = null;
         }
 
+        if (mAdvancedProtectionCoordinator != null) {
+            mAdvancedProtectionCoordinator.destroy();
+            mAdvancedProtectionCoordinator = null;
+        }
+
         super.onDestroy();
     }
 
@@ -739,6 +746,8 @@
         super.onFinishNativeInitialization();
         assert mLayoutManager != null;
 
+        mAdvancedProtectionCoordinator = new AdvancedProtectionCoordinator(mWindowAndroid);
+
         UmaSessionStats.registerSyntheticFieldTrial(
                 "AndroidNavigationMode",
                 UiUtils.isGestureNavigationMode(mActivity.getWindow())
@@ -1541,6 +1550,10 @@
             return true;
         }
 
+        if (mAdvancedProtectionCoordinator.showMessageOnStartupIfNeeded()) {
+            return true;
+        }
+
         return triggerPromo(profile, intentWithEffect);
     }
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 3de3e2f..96f3909a 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -252,8 +252,16 @@
             "Chrome.RequestDesktopSiteGlobalSetting.DefaultEnabled";
 
     /**
-     * Indicates that Chrome should show an alert to the user about data privacy if the device
-     * lock is removed.
+     * Indicates the state of the Android-OS-provided advanced-protection setting when Chrome was
+     * 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 =
             "Chrome.DeviceLock.ShowAlertIfRemoved";
@@ -1005,6 +1013,7 @@
                 DEFAULT_BROWSER_PROMO_PROMOED_COUNT,
                 DEFAULT_BROWSER_PROMO_SESSION_COUNT,
                 DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING,
+                DEFAULT_OS_ADVANCED_PROTECTION_SETTING,
                 DEPRECATED_HOMEPAGE_LOCATION_POLICY,
                 DEPRECATED_HOMEPAGE_PARTNER_CUSTOMIZED_DEFAULT_URI,
                 DEVICE_LOCK_SHOW_ALERT_IF_REMOVED,
diff --git a/chrome/browser/safe_browsing/android/BUILD.gn b/chrome/browser/safe_browsing/android/BUILD.gn
index 947aef5..7adfb06 100644
--- a/chrome/browser/safe_browsing/android/BUILD.gn
+++ b/chrome/browser/safe_browsing/android/BUILD.gn
@@ -44,6 +44,8 @@
 
 android_library("java") {
   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/settings/EnhancedProtectionSettingsFragment.java",
     "java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java",
@@ -55,6 +57,7 @@
   deps = [
     ":java_resources",
     "//base:base_java",
+    "//base:supplier_java",
     "//build/android:build_java",
     "//chrome/browser/feedback/android:java",
     "//chrome/browser/flags:java",
@@ -65,6 +68,9 @@
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/util/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/user_prefs/android:java",
     "//content/public/android:content_java",
@@ -129,6 +135,28 @@
   ]
 }
 
+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") {
   sources = [
     "java/res/layout/radio_button_group_safe_browsing_preference.xml",
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java
new file mode 100644
index 0000000..7df5e8a
--- /dev/null
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java
@@ -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();
+    }
+}
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
new file mode 100644
index 0000000..66bee182
--- /dev/null
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
@@ -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());
+    }
+}
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java
new file mode 100644
index 0000000..bfcba66
--- /dev/null
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java
@@ -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();
+    }
+}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java b/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
index dad5657..b5dd7be 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessagesMetrics.java
@@ -373,6 +373,8 @@
                 return "CctAccountMismatchNotice";
             case MessageIdentifier.PROMPT_HATS_CLEAR_BROWSING_DATA:
                 return "PromptHatsClearBrowsingData";
+            case MessageIdentifier.OS_ADVANCED_PROTECTION_SETTING_CHANGED_MESSAGE:
+                return "OsAdvancedProtectionSettingChangedMessage";
             default:
                 return "Unknown";
         }
diff --git a/components/messages/android/message_enums.h b/components/messages/android/message_enums.h
index 776fe74..af88289 100644
--- a/components/messages/android/message_enums.h
+++ b/components/messages/android/message_enums.h
@@ -142,6 +142,7 @@
   COLLABORATION_REMOVED = 57,
   CCT_ACCOUNT_MISMATCH_NOTICE = 58,
   PROMPT_HATS_CLEAR_BROWSING_DATA = 59,
+  OS_ADVANCED_PROTECTION_SETTING_CHANGED_MESSAGE = 60,
   // Insert new values before this line.
   COUNT
 };
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/OsAdditionalSecurityPermissionProvider.java b/components/permissions/android/java/src/org/chromium/components/permissions/OsAdditionalSecurityPermissionProvider.java
index ae08713..c89a5ce2 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/OsAdditionalSecurityPermissionProvider.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/OsAdditionalSecurityPermissionProvider.java
@@ -6,7 +6,10 @@
 
 import android.content.Context;
 
+import androidx.annotation.Nullable;
+
 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
@@ -14,11 +17,30 @@
  */
 @NullMarked
 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.
      * 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
@@ -27,4 +49,15 @@
     public String getJavascriptOptimizerMessage(Context context) {
         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;
+    }
 }
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 2f194378..12b2170 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1154,6 +1154,7 @@
   <int value="57" label="CollaborationRemoved"/>
   <int value="58" label="CctAccountMismatchNotice"/>
   <int value="59" label="PromptHatsClearBrowsingData"/>
+  <int value="60" label="OsAdvancedProtectionSettingChangedMessage"/>
 </enum>
 
 <!-- LINT.ThenChange(//components/messages/android/message_enums.h:MessageIdentifier) -->
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 7fbd26f..f52ae5d 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -177,6 +177,7 @@
   <variant name=".NearOomReduction"/>
   <variant name=".NotificationBlocked"/>
   <variant name=".OfferNotification"/>
+  <variant name=".OsAdvancedProtectionSettingChangedMessage"/>
   <variant name=".PermissionBlocked"/>
   <variant name=".PermissionUpdate"/>
   <variant name=".PopupBlocked"/>