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:

committed by
Chromium LUCI CQ

parent
0441655922
commit
7c00607962
chrome
android
browser
preferences
android
java
src
org
chromium
chrome
browser
preferences
safe_browsing
android
components
messages
android
permissions
android
java
src
org
chromium
components
permissions
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",
|
||||||
|
30
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java
Normal file
30
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionCoordinator.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
93
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
Normal file
93
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
249
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java
Normal file
249
chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorTest.java
Normal file
@ -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"/>
|
||||||
|
Reference in New Issue
Block a user