diff options
16 files changed, 1068 insertions, 164 deletions
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java index b1d6ac4b4cd2..32938ffae63d 100644 --- a/core/java/android/hardware/CameraSessionStats.java +++ b/core/java/android/hardware/CameraSessionStats.java @@ -65,6 +65,7 @@ public class CameraSessionStats implements Parcelable { private String mUserTag; private int mVideoStabilizationMode; private boolean mUsedUltraWide; + private boolean mUsedZoomOverride; private int mSessionIndex; private CameraExtensionSessionStats mCameraExtensionSessionStats; @@ -84,6 +85,7 @@ public class CameraSessionStats implements Parcelable { mStreamStats = new ArrayList<CameraStreamStats>(); mVideoStabilizationMode = -1; mUsedUltraWide = false; + mUsedZoomOverride = false; mSessionIndex = 0; mCameraExtensionSessionStats = new CameraExtensionSessionStats(); } @@ -106,6 +108,7 @@ public class CameraSessionStats implements Parcelable { mStreamStats = new ArrayList<CameraStreamStats>(); mVideoStabilizationMode = -1; mUsedUltraWide = false; + mUsedZoomOverride = false; mSessionIndex = sessionIdx; mCameraExtensionSessionStats = new CameraExtensionSessionStats(); } @@ -152,6 +155,7 @@ public class CameraSessionStats implements Parcelable { dest.writeString(mUserTag); dest.writeInt(mVideoStabilizationMode); dest.writeBoolean(mUsedUltraWide); + dest.writeBoolean(mUsedZoomOverride); dest.writeInt(mSessionIndex); mCameraExtensionSessionStats.writeToParcel(dest, 0); } @@ -180,6 +184,7 @@ public class CameraSessionStats implements Parcelable { mVideoStabilizationMode = in.readInt(); mUsedUltraWide = in.readBoolean(); + mUsedZoomOverride = in.readBoolean(); mSessionIndex = in.readInt(); mCameraExtensionSessionStats = CameraExtensionSessionStats.CREATOR.createFromParcel(in); @@ -257,6 +262,10 @@ public class CameraSessionStats implements Parcelable { return mUsedUltraWide; } + public boolean getUsedZoomOverride() { + return mUsedZoomOverride; + } + public int getSessionIndex() { return mSessionIndex; } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 57011e879454..5f612d6b0009 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -395,6 +395,22 @@ public final class ContentCaptureManager { public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD = "content_protection_optional_groups_threshold"; + /** + * Sets the initial delay for fetching content protection allowlist in milliseconds. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS = + "content_protection_allowlist_delay_ms"; + + /** + * Sets the timeout for fetching content protection allowlist in milliseconds. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS = + "content_protection_allowlist_timeout_ms"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -445,6 +461,10 @@ public final class ContentCaptureManager { public static final String DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG = ""; /** @hide */ public static final int DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD = 0; + /** @hide */ + public static final long DEFAULT_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS = 30000; + /** @hide */ + public static final long DEFAULT_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS = 250; private final Object mLock = new Object(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 37bcf1ddeac5..0568edaa7ab6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -949,7 +949,9 @@ public class BubbleExpandedView extends LinearLayout { if (mTaskView != null && mTaskView.getVisibility() == VISIBLE && mTaskView.isAttachedToWindow()) { - mTaskView.onLocationChanged(); + // post this to the looper, because if the device orientation just changed, we need to + // let the current shell transition complete before updating the task view bounds. + post(() -> mTaskView.onLocationChanged()); } if (mIsOverflow) { // post this to the looper so that the view has a chance to be laid out before it can diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 3660fa29e9e4..9402d028ecc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1012,15 +1012,15 @@ public class BubbleStackView extends FrameLayout } if (mIsExpanded) { - // Re-draw bubble row and pointer for new orientation. - beforeExpandedViewAnimation(); + // update the expanded view and pointer location for the new orientation. + hideFlyoutImmediate(); + mExpandedViewContainer.setAlpha(0f); + updateExpandedView(); updateOverflowVisibility(); - updatePointerPosition(false /* forIme */); - mExpandedAnimationController.expandFromStack(() -> { - afterExpandedViewAnimation(); - mExpandedViewContainer.setVisibility(VISIBLE); - showManageMenu(mShowingManage); - } /* after */); + updatePointerPosition(false); + requestUpdate(); + showManageMenu(mShowingManage); + PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble), getState()); final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, @@ -1029,6 +1029,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setTranslationY(translationY); mExpandedViewContainer.setAlpha(1f); } + removeOnLayoutChangeListener(mOrientationChangedListener); }; final float maxDismissSize = getResources().getDimensionPixelSize( diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index acfb259d7359..bd2eb5b4a8ae 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -153,9 +153,8 @@ java_library { } java_defaults { - name: "WMShellFlickerTestsDefault", + name: "WMShellFlickerTestsDefaultWithoutTemplate", manifest: "manifests/AndroidManifest.xml", - test_config_template: "AndroidTestTemplate.xml", platform_apis: true, certificate: "platform", optimize: { @@ -182,6 +181,12 @@ java_defaults { ], } +java_defaults { + name: "WMShellFlickerTestsDefault", + defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"], + test_config_template: "AndroidTestTemplate.xml", +} + android_test { name: "WMShellFlickerTestsOther", defaults: ["WMShellFlickerTestsDefault"], @@ -276,6 +281,23 @@ android_test { } android_test { + name: "WMShellFlickerTestsPipAppsCSuite", + defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip.apps", + instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPipApps-src", + ":WMShellFlickerTestsPipCommon-src", + ], + test_suites: [ + "device-tests", + "csuite", + ], +} + +android_test { name: "WMShellFlickerTestsSplitScreenGroup1", defaults: ["WMShellFlickerTestsDefault"], additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], @@ -327,3 +349,8 @@ android_test { ":WMShellFlickerServicePlatinumTests-src", ], } + +csuite_test { + name: "csuite-1p3p-pip-flickers", + test_config_template: "csuiteDefaultTemplate.xml", +} diff --git a/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml new file mode 100644 index 000000000000..ca182fa5a266 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<configuration description="Runs WindowManager Shell Flicker Tests WMShellFlickerTestsPipAppsCSuite"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="WMShellFlickerTestsPipAppsCSuite.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.wm.shell.flicker.pip.apps"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> + + <!-- Needed for installing apk's from Play Store --> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="aapt-version" value="AAPT2"/> + <option name="throw-if-not-found" value="false"/> + <option name="install-arg" value="-d"/> + <option name="install-arg" value="-g"/> + <option name="install-arg" value="-r"/> + <option name="test-file-name" value="pstash://com.netflix.mediaclient"/> + </target_preparer> +</configuration> diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index de7ea701b26d..91f78ce6f950 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -21,22 +21,20 @@ #include <android/font.h> #include <android/font_matcher.h> #include <android/system_fonts.h> - -#include <memory> -#include <string> -#include <vector> - #include <errno.h> #include <fcntl.h> -#include <libxml/tree.h> -#include <log/log.h> -#include <sys/stat.h> -#include <unistd.h> - #include <hwui/MinikinSkia.h> +#include <libxml/parser.h> +#include <log/log.h> #include <minikin/FontCollection.h> #include <minikin/LocaleList.h> #include <minikin/SystemFonts.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <memory> +#include <string> +#include <vector> struct XmlCharDeleter { void operator()(xmlChar* b) { xmlFree(b); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index 95b467fdad40..5c590870981d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -45,30 +45,61 @@ object NotificationListViewBinder { iconAreaController: NotificationIconAreaController, shelfIconViewStore: ShelfNotificationIconViewStore, ) { + bindShelf( + view, + viewModel, + configuration, + configurationController, + falsingManager, + iconAreaController, + shelfIconViewStore + ) + + bindFooter(view, viewModel, configuration) + } + + private fun bindShelf( + parentView: NotificationStackScrollLayout, + parentViewModel: NotificationListViewModel, + configuration: ConfigurationState, + configurationController: ConfigurationController, + falsingManager: FalsingManager, + iconAreaController: NotificationIconAreaController, + shelfIconViewStore: ShelfNotificationIconViewStore + ) { val shelf = - LayoutInflater.from(view.context) - .inflate(R.layout.status_bar_notification_shelf, view, false) as NotificationShelf + LayoutInflater.from(parentView.context) + .inflate(R.layout.status_bar_notification_shelf, parentView, false) + as NotificationShelf NotificationShelfViewBinder.bind( shelf, - viewModel.shelf, + parentViewModel.shelf, configuration, configurationController, falsingManager, iconAreaController, shelfIconViewStore, ) - view.setShelf(shelf) + parentView.setShelf(shelf) + } - viewModel.footer.ifPresent { footerViewModel -> + private fun bindFooter( + parentView: NotificationStackScrollLayout, + parentViewModel: NotificationListViewModel, + configuration: ConfigurationState + ) { + parentViewModel.footer.ifPresent { footerViewModel -> // The footer needs to be re-inflated every time the theme or the font size changes. - view.repeatWhenAttached { + parentView.repeatWhenAttached { configuration.reinflateAndBindLatest( R.layout.status_bar_notification_footer, - view, + parentView, attachToRoot = false, ) { footerView: FooterView -> traceSection("bind FooterView") { - FooterViewBinder.bind(footerView, footerViewModel) + val disposableHandle = FooterViewBinder.bind(footerView, footerViewModel) + parentView.setFooterView(footerView) + return@reinflateAndBindLatest disposableHandle } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 76ebdf4d4c1c..9f4528b113e7 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -20,6 +20,8 @@ import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE; import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE; import static android.service.contentcapture.ContentCaptureService.setClientState; import static android.view.contentcapture.ContentCaptureHelper.toList; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG; @@ -219,6 +221,12 @@ public class ContentCaptureManagerService extends @GuardedBy("mLock") int mDevCfgContentProtectionOptionalGroupsThreshold; + @GuardedBy("mLock") + long mDevCfgContentProtectionAllowlistDelayMs; + + @GuardedBy("mLock") + long mDevCfgContentProtectionAllowlistTimeoutMs; + private final Executor mDataShareExecutor = Executors.newCachedThreadPool(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -231,11 +239,17 @@ public class ContentCaptureManagerService extends final GlobalContentCaptureOptions mGlobalContentCaptureOptions = new GlobalContentCaptureOptions(); - @Nullable private final ComponentName mContentProtectionServiceComponentName; + @GuardedBy("mLock") + @Nullable + private ComponentName mContentProtectionServiceComponentName; - @Nullable private final ContentProtectionAllowlistManager mContentProtectionAllowlistManager; + @GuardedBy("mLock") + @Nullable + private ContentProtectionAllowlistManager mContentProtectionAllowlistManager; - @Nullable private final ContentProtectionConsentManager mContentProtectionConsentManager; + @GuardedBy("mLock") + @Nullable + private ContentProtectionConsentManager mContentProtectionConsentManager; public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, @@ -279,21 +293,6 @@ public class ContentCaptureManagerService extends mServiceNameResolver.getServiceName(userId), mServiceNameResolver.isTemporary(userId)); } - - if (getEnableContentProtectionReceiverLocked()) { - mContentProtectionServiceComponentName = getContentProtectionServiceComponentName(); - if (mContentProtectionServiceComponentName != null) { - mContentProtectionAllowlistManager = createContentProtectionAllowlistManager(); - mContentProtectionConsentManager = createContentProtectionConsentManager(); - } else { - mContentProtectionAllowlistManager = null; - mContentProtectionConsentManager = null; - } - } else { - mContentProtectionServiceComponentName = null; - mContentProtectionAllowlistManager = null; - mContentProtectionConsentManager = null; - } } @Override // from AbstractMasterSystemService @@ -442,6 +441,8 @@ public class ContentCaptureManagerService extends case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG: case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG: case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD: + case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS: + case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS: setFineTuneParamsFromDeviceConfig(); return; default: @@ -453,8 +454,15 @@ public class ContentCaptureManagerService extends /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected void setFineTuneParamsFromDeviceConfig() { + boolean enableContentProtectionReceiverOld; + boolean enableContentProtectionReceiverNew; String contentProtectionRequiredGroupsConfig; String contentProtectionOptionalGroupsConfig; + int contentProtectionOptionalGroupsThreshold; + long contentProtectionAllowlistDelayMs; + long contentProtectionAllowlistTimeoutMs; + ContentProtectionAllowlistManager contentProtectionAllowlistManagerOld; + synchronized (mLock) { mDevCfgMaxBufferSize = DeviceConfig.getInt( @@ -488,12 +496,9 @@ public class ContentCaptureManagerService extends ContentCaptureManager .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, false); - mDevCfgEnableContentProtectionReceiver = - DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager - .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER, - ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER); + + enableContentProtectionReceiverOld = mDevCfgEnableContentProtectionReceiver; + enableContentProtectionReceiverNew = getDeviceConfigEnableContentProtectionReceiver(); mDevCfgContentProtectionBufferSize = DeviceConfig.getInt( DeviceConfig.NAMESPACE_CONTENT_CAPTURE, @@ -512,12 +517,25 @@ public class ContentCaptureManagerService extends DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG, ContentCaptureManager .DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG); - mDevCfgContentProtectionOptionalGroupsThreshold = + contentProtectionOptionalGroupsThreshold = DeviceConfig.getInt( DeviceConfig.NAMESPACE_CONTENT_CAPTURE, DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD, ContentCaptureManager .DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD); + contentProtectionAllowlistDelayMs = + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS); + contentProtectionAllowlistTimeoutMs = + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS); + + contentProtectionAllowlistManagerOld = mContentProtectionAllowlistManager; + if (verbose) { Slog.v( TAG, @@ -535,7 +553,7 @@ public class ContentCaptureManagerService extends + ", disableFlushForViewTreeAppearing=" + mDevCfgDisableFlushForViewTreeAppearing + ", enableContentProtectionReceiver=" - + mDevCfgEnableContentProtectionReceiver + + enableContentProtectionReceiverNew + ", contentProtectionBufferSize=" + mDevCfgContentProtectionBufferSize + ", contentProtectionRequiredGroupsConfig=" @@ -543,7 +561,11 @@ public class ContentCaptureManagerService extends + ", contentProtectionOptionalGroupsConfig=" + contentProtectionOptionalGroupsConfig + ", contentProtectionOptionalGroupsThreshold=" - + mDevCfgContentProtectionOptionalGroupsThreshold); + + contentProtectionOptionalGroupsThreshold + + ", contentProtectionAllowlistDelayMs=" + + contentProtectionAllowlistDelayMs + + ", contentProtectionAllowlistTimeoutMs=" + + contentProtectionAllowlistTimeoutMs); } } @@ -551,9 +573,37 @@ public class ContentCaptureManagerService extends parseContentProtectionGroupsConfig(contentProtectionRequiredGroupsConfig); List<List<String>> contentProtectionOptionalGroups = parseContentProtectionGroupsConfig(contentProtectionOptionalGroupsConfig); + ComponentName contentProtectionServiceComponentNameNew = null; + ContentProtectionAllowlistManager contentProtectionAllowlistManagerNew = null; + ContentProtectionConsentManager contentProtectionConsentManagerNew = null; + + if (contentProtectionAllowlistManagerOld != null && !enableContentProtectionReceiverNew) { + contentProtectionAllowlistManagerOld.stop(); + } + if (!enableContentProtectionReceiverOld && enableContentProtectionReceiverNew) { + contentProtectionServiceComponentNameNew = getContentProtectionServiceComponentName(); + if (contentProtectionServiceComponentNameNew != null) { + contentProtectionAllowlistManagerNew = + createContentProtectionAllowlistManager( + contentProtectionAllowlistTimeoutMs); + contentProtectionAllowlistManagerNew.start(contentProtectionAllowlistDelayMs); + contentProtectionConsentManagerNew = createContentProtectionConsentManager(); + } + } + synchronized (mLock) { + mDevCfgEnableContentProtectionReceiver = enableContentProtectionReceiverNew; mDevCfgContentProtectionRequiredGroups = contentProtectionRequiredGroups; mDevCfgContentProtectionOptionalGroups = contentProtectionOptionalGroups; + mDevCfgContentProtectionOptionalGroupsThreshold = + contentProtectionOptionalGroupsThreshold; + mDevCfgContentProtectionAllowlistDelayMs = contentProtectionAllowlistDelayMs; + + if (enableContentProtectionReceiverOld ^ enableContentProtectionReceiverNew) { + mContentProtectionServiceComponentName = contentProtectionServiceComponentNameNew; + mContentProtectionAllowlistManager = contentProtectionAllowlistManagerNew; + mContentProtectionConsentManager = contentProtectionConsentManagerNew; + } } } @@ -837,27 +887,34 @@ public class ContentCaptureManagerService extends pw.print(prefix2); pw.print("contentProtectionOptionalGroupsThreshold: "); pw.println(mDevCfgContentProtectionOptionalGroupsThreshold); + pw.print(prefix2); + pw.print("contentProtectionAllowlistDelayMs: "); + pw.println(mDevCfgContentProtectionAllowlistDelayMs); + pw.print(prefix2); + pw.print("contentProtectionAllowlistTimeoutMs: "); + pw.println(mDevCfgContentProtectionAllowlistTimeoutMs); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); } - /** - * Used by the constructor in order to be able to override the value in the tests. - * - * @hide - */ + /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - @GuardedBy("mLock") - protected boolean getEnableContentProtectionReceiverLocked() { - return mDevCfgEnableContentProtectionReceiver; + protected boolean getDeviceConfigEnableContentProtectionReceiver() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER); } /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @NonNull - protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager() { - return new ContentProtectionAllowlistManager(); + protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager( + long timeoutMs) { + // Same handler as used by AbstractMasterSystemService + return new ContentProtectionAllowlistManager( + this, BackgroundThread.getHandler(), timeoutMs); } /** @hide */ @@ -874,6 +931,9 @@ public class ContentCaptureManagerService extends @Nullable private ComponentName getContentProtectionServiceComponentName() { String flatComponentName = getContentProtectionServiceFlatComponentName(); + if (flatComponentName == null) { + return null; + } return ComponentName.unflattenFromString(flatComponentName); } @@ -898,27 +958,27 @@ public class ContentCaptureManagerService extends getContext(), componentName, /* isTemp= */ false, UserHandle.getCallingUserId()); } + /** @hide */ @Nullable - private RemoteContentProtectionService createRemoteContentProtectionService() { - if (mContentProtectionServiceComponentName == null) { - // This case should not be possible but make sure - return null; - } + public RemoteContentProtectionService createRemoteContentProtectionService() { + ComponentName componentName; synchronized (mLock) { - if (!mDevCfgEnableContentProtectionReceiver) { + if (!mDevCfgEnableContentProtectionReceiver + || mContentProtectionServiceComponentName == null) { return null; } + componentName = mContentProtectionServiceComponentName; } // Check permissions by trying to construct {@link ContentCaptureServiceInfo} try { - createContentProtectionServiceInfo(mContentProtectionServiceComponentName); + createContentProtectionServiceInfo(componentName); } catch (Exception ex) { // Swallow, exception was already logged return null; } - return createRemoteContentProtectionService(mContentProtectionServiceComponentName); + return createRemoteContentProtectionService(componentName); } /** @hide */ @@ -976,6 +1036,16 @@ public class ContentCaptureManagerService extends .toList(); } + @GuardedBy("mLock") + private boolean isContentProtectionEnabledLocked() { + return mDevCfgEnableContentProtectionReceiver + && mContentProtectionServiceComponentName != null + && mContentProtectionAllowlistManager != null + && mContentProtectionConsentManager != null + && !(mDevCfgContentProtectionRequiredGroups.isEmpty() + && mDevCfgContentProtectionOptionalGroups.isEmpty()); + } + final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub { @Override @@ -1406,22 +1476,17 @@ public class ContentCaptureManagerService extends private boolean isContentProtectionReceiverEnabled( @UserIdInt int userId, @NonNull String packageName) { - if (mContentProtectionServiceComponentName == null - || mContentProtectionAllowlistManager == null - || mContentProtectionConsentManager == null) { - return false; - } + ContentProtectionConsentManager consentManager; + ContentProtectionAllowlistManager allowlistManager; synchronized (mLock) { - if (!mDevCfgEnableContentProtectionReceiver) { - return false; - } - if (mDevCfgContentProtectionRequiredGroups.isEmpty() - && mDevCfgContentProtectionOptionalGroups.isEmpty()) { + if (!isContentProtectionEnabledLocked()) { return false; } + consentManager = mContentProtectionConsentManager; + allowlistManager = mContentProtectionAllowlistManager; } - return mContentProtectionConsentManager.isConsentGranted(userId) - && mContentProtectionAllowlistManager.isAllowed(packageName); + return consentManager.isConsentGranted(userId) + && allowlistManager.isAllowed(packageName); } } diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java index 59af5263fa9e..f77430dae26b 100644 --- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java +++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java @@ -16,8 +16,22 @@ package com.android.server.contentprotection; +import static android.view.contentprotection.flags.Flags.blocklistUpdateEnabled; + import android.annotation.NonNull; -import android.util.Slog; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.UserHandle; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.content.PackageMonitor; +import com.android.server.contentcapture.ContentCaptureManagerService; + +import java.time.Instant; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * Manages whether the content protection is enabled for an app using a allowlist. @@ -28,11 +42,124 @@ public class ContentProtectionAllowlistManager { private static final String TAG = "ContentProtectionAllowlistManager"; - public ContentProtectionAllowlistManager() {} + @NonNull private final ContentCaptureManagerService mContentCaptureManagerService; + + @NonNull private final Handler mHandler; + + private final long mTimeoutMs; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @NonNull + final PackageMonitor mPackageMonitor; + + private final Object mHandlerToken = new Object(); + + private final Object mLock = new Object(); + + // Used outside of the handler + private boolean mStarted; + + // Used inside the handler + @Nullable private Instant mUpdatePendingUntil; + + @NonNull + @GuardedBy("mLock") + private Set<String> mAllowedPackages = Set.of(); + + public ContentProtectionAllowlistManager( + @NonNull ContentCaptureManagerService contentCaptureManagerService, + @NonNull Handler handler, + long timeoutMs) { + mContentCaptureManagerService = contentCaptureManagerService; + mHandler = handler; + mTimeoutMs = timeoutMs; + mPackageMonitor = createPackageMonitor(); + } + + /** Starts the manager. */ + public void start(long delayMs) { + if (mStarted) { + return; + } + mStarted = true; + mHandler.postDelayed(this::handleInitialUpdate, mHandlerToken, delayMs); + // PackageMonitor will be registered inside handleInitialUpdate to respect the initial delay + } + + /** Stops the manager. */ + public void stop() { + try { + mPackageMonitor.unregister(); + } catch (IllegalStateException ex) { + // Swallow, throws if not registered + } + mHandler.removeCallbacksAndMessages(mHandlerToken); + mUpdatePendingUntil = null; + mStarted = false; + } /** Returns true if the package is allowed. */ public boolean isAllowed(@NonNull String packageName) { - Slog.v(TAG, packageName); - return false; + Set<String> allowedPackages; + synchronized (mLock) { + allowedPackages = mAllowedPackages; + } + return allowedPackages.contains(packageName); + } + + private void setAllowlist(@NonNull List<String> packages) { + synchronized (mLock) { + mAllowedPackages = packages.stream().collect(Collectors.toUnmodifiableSet()); + } + mUpdatePendingUntil = null; + } + + private void handleInitialUpdate() { + handleUpdate(); + + // Initial update done, start listening to package updates now + mPackageMonitor.register( + mContentCaptureManagerService.getContext(), UserHandle.ALL, mHandler); + } + + private void handleUpdate() { + if (!blocklistUpdateEnabled()) { + return; + } + + /** + * PackageMonitor callback can be invoked more than once in a matter of milliseconds on the + * same monitor instance for the same package (eg: b/295969873). This check acts both as a + * simple generic rate limit and as a mitigation for this quirk. + */ + if (mUpdatePendingUntil != null && Instant.now().isBefore(mUpdatePendingUntil)) { + return; + } + + RemoteContentProtectionService remoteContentProtectionService = + mContentCaptureManagerService.createRemoteContentProtectionService(); + if (remoteContentProtectionService == null) { + return; + } + + // If there are any pending updates queued already, they can be removed immediately + mHandler.removeCallbacksAndMessages(mHandlerToken); + mUpdatePendingUntil = Instant.now().plusMillis(mTimeoutMs); + } + + /** @hide */ + @NonNull + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected PackageMonitor createPackageMonitor() { + return new ContentProtectionPackageMonitor(); + } + + private final class ContentProtectionPackageMonitor extends PackageMonitor { + + // This callback might be invoked multiple times, for more info refer to the comment above + @Override + public void onSomePackagesChanged() { + handleUpdate(); + } } } diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java index 2eb758cd5a13..488a51a56fee 100644 --- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java +++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java @@ -40,6 +40,9 @@ public class ContentProtectionConsentManager { private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent"; + private static final String KEY_CONTENT_PROTECTION_USER_CONSENT = + "content_protection_user_consent"; + @NonNull private final ContentResolver mContentResolver; @NonNull private final DevicePolicyManagerInternal mDevicePolicyManagerInternal; @@ -50,6 +53,8 @@ public class ContentProtectionConsentManager { private volatile boolean mCachedPackageVerifierConsent; + private volatile boolean mCachedContentProtectionConsent; + public ContentProtectionConsentManager( @NonNull Handler handler, @NonNull ContentResolver contentResolver, @@ -63,14 +68,18 @@ public class ContentProtectionConsentManager { /* notifyForDescendants= */ false, mContentObserver, UserHandle.USER_ALL); + mCachedPackageVerifierConsent = isPackageVerifierConsentGranted(); + mCachedContentProtectionConsent = isContentProtectionConsentGranted(); } /** * Returns true if all the consents are granted */ public boolean isConsentGranted(@UserIdInt int userId) { - return mCachedPackageVerifierConsent && !isUserOrganizationManaged(userId); + return mCachedPackageVerifierConsent + && mCachedContentProtectionConsent + && !isUserOrganizationManaged(userId); } private boolean isPackageVerifierConsentGranted() { @@ -80,6 +89,13 @@ public class ContentProtectionConsentManager { >= 1; } + private boolean isContentProtectionConsentGranted() { + // Not always cached internally + return Settings.Global.getInt( + mContentResolver, KEY_CONTENT_PROTECTION_USER_CONSENT, /* def= */ 0) + >= 0; + } + private boolean isUserOrganizationManaged(@UserIdInt int userId) { // Cached internally return mDevicePolicyManagerInternal.isUserOrganizationManaged(userId); @@ -101,6 +117,9 @@ public class ContentProtectionConsentManager { case KEY_PACKAGE_VERIFIER_USER_CONSENT: mCachedPackageVerifierConsent = isPackageVerifierConsentGranted(); return; + case KEY_CONTENT_PROTECTION_USER_CONSENT: + mCachedContentProtectionConsent = isContentProtectionConsentGranted(); + return; default: Slog.w(TAG, "Ignoring unexpected property: " + property); } diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index f9bc8dcc7d0b..5bb5c53f6d5e 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -243,6 +243,7 @@ public class CameraServiceProxy extends SystemService public String mUserTag; public int mVideoStabilizationMode; public boolean mUsedUltraWide; + public boolean mUsedZoomOverride; public final long mLogId; public final int mSessionIndex; @@ -271,7 +272,7 @@ public class CameraServiceProxy extends SystemService long resultErrorCount, boolean deviceError, List<CameraStreamStats> streamStats, String userTag, int videoStabilizationMode, boolean usedUltraWide, - CameraExtensionSessionStats extStats) { + boolean usedZoomOverride, CameraExtensionSessionStats extStats) { if (mCompleted) { return; } @@ -285,6 +286,7 @@ public class CameraServiceProxy extends SystemService mUserTag = userTag; mVideoStabilizationMode = videoStabilizationMode; mUsedUltraWide = usedUltraWide; + mUsedZoomOverride = usedZoomOverride; mExtSessionStats = extStats; if (CameraServiceProxy.DEBUG) { Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) + @@ -877,6 +879,9 @@ public class CameraServiceProxy extends SystemService String ultrawideDebug = Flags.logUltrawideUsage() ? ", wideAngleUsage " + e.mUsedUltraWide : ""; + String zoomOverrideDebug = Flags.logZoomOverrideUsage() + ? ", zoomOverrideUsage " + e.mUsedZoomOverride + : ""; Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction + " clientName " + e.mClientName @@ -895,6 +900,7 @@ public class CameraServiceProxy extends SystemService + ", userTag is " + e.mUserTag + ", videoStabilizationMode " + e.mVideoStabilizationMode + ultrawideDebug + + zoomOverrideDebug + ", logId " + e.mLogId + ", sessionIndex " + e.mSessionIndex + ", mExtSessionStats {type " + extensionType @@ -960,7 +966,8 @@ public class CameraServiceProxy extends SystemService MessageNano.toByteArray(streamProtos[4]), e.mUserTag, e.mVideoStabilizationMode, e.mLogId, e.mSessionIndex, - extensionType, extensionIsAdvanced, e.mUsedUltraWide); + extensionType, extensionIsAdvanced, e.mUsedUltraWide, + e.mUsedZoomOverride); } } @@ -1158,6 +1165,8 @@ public class CameraServiceProxy extends SystemService String userTag = cameraState.getUserTag(); int videoStabilizationMode = cameraState.getVideoStabilizationMode(); boolean usedUltraWide = Flags.logUltrawideUsage() ? cameraState.getUsedUltraWide() : false; + boolean usedZoomOverride = + Flags.logZoomOverrideUsage() ? cameraState.getUsedZoomOverride() : false; long logId = cameraState.getLogId(); int sessionIdx = cameraState.getSessionIndex(); CameraExtensionSessionStats extSessionStats = cameraState.getExtensionSessionStats(); @@ -1216,7 +1225,7 @@ public class CameraServiceProxy extends SystemService oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0, /*resultErrorCount*/0, /*deviceError*/false, streamStats, /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false, - new CameraExtensionSessionStats()); + /*usedZoomOverride*/false, new CameraExtensionSessionStats()); mCameraUsageHistory.add(oldEvent); } break; @@ -1227,7 +1236,8 @@ public class CameraServiceProxy extends SystemService doneEvent.markCompleted(internalReconfigureCount, requestCount, resultErrorCount, deviceError, streamStats, userTag, - videoStabilizationMode, usedUltraWide, extSessionStats); + videoStabilizationMode, usedUltraWide, usedZoomOverride, + extSessionStats); mCameraUsageHistory.add(doneEvent); // Do not double count device error deviceError = false; diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 9b7b8de633c4..cb4cf9d3162c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -356,15 +356,23 @@ public class BackgroundActivityStartController { public String toString() { StringBuilder builder = new StringBuilder(); - if (mBackground) { - builder.append("Background "); - } - builder.append("Activity start allowed: " + mMessage + "."); - builder.append("BAL Code: "); + builder.append(". BAL Code: "); builder.append(balCodeToString(mCode)); - if (mProcessInfo != null) { + if (DEBUG_ACTIVITY_STARTS) { builder.append(" "); - builder.append(mProcessInfo); + if (mBackground) { + builder.append("Background "); + } + builder.append("Activity start "); + if (mCode == BAL_BLOCK) { + builder.append("denied"); + } else { + builder.append("allowed: ").append(mMessage); + } + if (mProcessInfo != null) { + builder.append(" "); + builder.append(mProcessInfo); + } } return builder.toString(); } diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java index 78bf9b0bc828..9a5241ea242a 100644 --- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java @@ -21,6 +21,7 @@ import static android.view.contentprotection.flags.Flags.FLAG_PARSE_GROUPS_CONFI import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -31,7 +32,6 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; -import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.platform.test.flag.junit.SetFlagsRule; @@ -130,63 +130,154 @@ public class ContentCaptureManagerServiceTest { } @Test - public void constructor_contentProtection_flagDisabled_noManagers() { + public void constructor_contentProtection_disabled_noManagers() { assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); - assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @Test - public void constructor_contentProtection_componentNameNull_noManagers() { - mConfigDefaultContentProtectionService = null; + public void constructor_contentProtection_enabled_createsManagers() { + mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager, never()).stop(); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_contentProtection_disabled_to_disabled() { + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verifyZeroInteractions(mMockContentProtectionAllowlistManager); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_contentProtection_disabled_to_enabled() { + mDevCfgEnableContentProtectionReceiver = true; + + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager, never()).stop(); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_contentProtection_enabled_to_enabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager, never()).stop(); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_contentProtection_enabled_to_disabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + mDevCfgEnableContentProtectionReceiver = false; + + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager).stop(); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_contentProtection_enabled_componentNameNull() { + mDevCfgEnableContentProtectionReceiver = true; + mConfigDefaultContentProtectionService = null; + + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @Test - public void constructor_contentProtection_componentNameBlank_noManagers() { + public void setFineTuneParamsFromDeviceConfig_contentProtection_enabled_componentNameBlank() { + mDevCfgEnableContentProtectionReceiver = true; mConfigDefaultContentProtectionService = " "; - mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); - assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @Test - public void constructor_contentProtection_enabled_createsManagers() { + public void setFineTuneParamsFromDeviceConfig_contentProtection_disabled_componentNameNull() { mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + mDevCfgEnableContentProtectionReceiver = false; + mConfigDefaultContentProtectionService = null; - mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager).stop(); verifyZeroInteractions(mMockContentProtectionConsentManager); } @Test - public void getOptions_contentCaptureDisabled_contentProtectionDisabled() { + public void setFineTuneParamsFromDeviceConfig_contentProtection_disabled_componentNameBlank() { mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + mDevCfgEnableContentProtectionReceiver = false; + mConfigDefaultContentProtectionService = " "; + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionAllowlistManager).start(anyLong()); + verify(mMockContentProtectionAllowlistManager).stop(); + verifyZeroInteractions(mMockContentProtectionConsentManager); + } + + @Test + public void getOptions_contentCaptureDisabled_contentProtectionDisabled() { ContentCaptureOptions actual = mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( USER_ID, PACKAGE_NAME); assertThat(actual).isNull(); - verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @@ -210,8 +301,6 @@ public class ContentCaptureManagerServiceTest { @Test public void getOptions_contentCaptureEnabled_contentProtectionDisabled() { - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); @@ -224,7 +313,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual.contentProtectionOptions).isNotNull(); assertThat(actual.contentProtectionOptions.enableReceiver).isFalse(); assertThat(actual.whitelistedComponents).isNull(); - verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @@ -249,22 +338,20 @@ public class ContentCaptureManagerServiceTest { } @Test - public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionNotGranted() { - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); - + public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionDisabled() { boolean actual = mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, PACKAGE_NAME); assertThat(actual).isFalse(); - verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test - public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionDisabled() { + public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -272,26 +359,25 @@ public class ContentCaptureManagerServiceTest { mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, PACKAGE_NAME); - assertThat(actual).isFalse(); - verify(mMockContentProtectionAllowlistManager).isAllowed(PACKAGE_NAME); + assertThat(actual).isTrue(); } @Test - public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() { - when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); + public void isWhitelisted_packageName_contentCaptureEnabled_contentProtectionDisabled() { + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); boolean actual = mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, PACKAGE_NAME); assertThat(actual).isTrue(); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test - public void isWhitelisted_packageName_contentCaptureEnabled_contentProtectionNotChecked() { + public void isWhitelisted_packageName_contentCaptureEnabled_contentProtectionEnabled() { mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( @@ -307,22 +393,20 @@ public class ContentCaptureManagerServiceTest { } @Test - public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionNotGranted() { - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); - + public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionDisabled() { boolean actual = mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, COMPONENT_NAME); assertThat(actual).isFalse(); - verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test - public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionDisabled() { + public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -330,26 +414,25 @@ public class ContentCaptureManagerServiceTest { mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, COMPONENT_NAME); - assertThat(actual).isFalse(); - verify(mMockContentProtectionAllowlistManager).isAllowed(PACKAGE_NAME); + assertThat(actual).isTrue(); } @Test - public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() { - when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); + public void isWhitelisted_componentName_contentCaptureEnabled_contentProtectionDisabled() { + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, /* packageNames= */ null, ImmutableList.of(COMPONENT_NAME)); boolean actual = mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( USER_ID, COMPONENT_NAME); assertThat(actual).isTrue(); + verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test - public void isWhitelisted_componentName_contentCaptureEnabled_contentProtectionNotChecked() { + public void isWhitelisted_componentName_contentCaptureEnabled_contentProtectionEnabled() { mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( @@ -544,8 +627,6 @@ public class ContentCaptureManagerServiceTest { TestContentCaptureManagerService() { super(sContext); - this.mDevCfgEnableContentProtectionReceiver = - ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver; this.mDevCfgContentProtectionRequiredGroups = ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionRequiredGroups; this.mDevCfgContentProtectionOptionalGroups = @@ -553,12 +634,13 @@ public class ContentCaptureManagerServiceTest { } @Override - protected boolean getEnableContentProtectionReceiverLocked() { + protected boolean getDeviceConfigEnableContentProtectionReceiver() { return ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver; } @Override - protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager() { + protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager( + long timeoutMs) { mContentProtectionAllowlistManagersCreated++; return mMockContentProtectionAllowlistManager; } @@ -570,7 +652,7 @@ public class ContentCaptureManagerServiceTest { @Override protected ContentCaptureServiceInfo createContentProtectionServiceInfo( - @NonNull ComponentName componentName) throws PackageManager.NameNotFoundException { + @NonNull ComponentName componentName) { mContentProtectionServiceInfosCreated++; if (mContentProtectionServiceInfoConstructorShouldThrow) { throw new RuntimeException("TEST RUNTIME EXCEPTION"); diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java index 6767a85035fe..dc38f2bf3083 100644 --- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java @@ -16,15 +16,35 @@ package com.android.server.contentprotection; +import static android.view.contentprotection.flags.Flags.FLAG_BLOCKLIST_UPDATE_ENABLED; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.os.Handler; +import android.os.UserHandle; +import android.os.test.TestLooper; +import android.platform.test.flag.junit.SetFlagsRule; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.internal.content.PackageMonitor; +import com.android.server.contentcapture.ContentCaptureManagerService; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -40,13 +60,236 @@ public class ContentProtectionAllowlistManagerTest { private static final String PACKAGE_NAME = "com.test.package.name"; + private static final long TIMEOUT_MS = 111_111_111L; + + private static final long DELAY_MS = 222_222_222L; + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Mock private ContentCaptureManagerService mMockContentCaptureManagerService; + + @Mock private PackageMonitor mMockPackageMonitor; + + @Mock private RemoteContentProtectionService mMockRemoteContentProtectionService; + + private final TestLooper mTestLooper = new TestLooper(); + + private Handler mHandler; + private ContentProtectionAllowlistManager mContentProtectionAllowlistManager; @Before public void setup() { - mContentProtectionAllowlistManager = new ContentProtectionAllowlistManager(); + mHandler = new Handler(mTestLooper.getLooper()); + mContentProtectionAllowlistManager = new TestContentProtectionAllowlistManager(); + } + + @Test + public void constructor() { + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verifyZeroInteractions(mMockPackageMonitor); + } + + @Test + public void start_updateEnabled_firstTime_beforeDelay() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.dispatchAll(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isTrue(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verifyZeroInteractions(mMockPackageMonitor); + } + + @Test + public void start_updateEnabled_firstTime_afterDelay() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verify(mMockContentCaptureManagerService).createRemoteContentProtectionService(); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor, never()).unregister(); + } + + @Test + public void start_updateEnabled_secondTime() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + mContentProtectionAllowlistManager.start(DELAY_MS); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verify(mMockContentCaptureManagerService).createRemoteContentProtectionService(); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor, never()).unregister(); + } + + @Test + public void start_updateDisabled_firstTime_beforeDelay() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.dispatchAll(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isTrue(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verifyZeroInteractions(mMockPackageMonitor); + } + + @Test + public void start_updateDisabled_firstTime_afterDelay() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor, never()).unregister(); + } + + @Test + public void start_updateDisabled_secondTime() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + mContentProtectionAllowlistManager.start(DELAY_MS); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor, never()).unregister(); + } + + @Test + public void stop_updateEnabled_notStarted() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + doThrow(new IllegalStateException("NOT REGISTERED")).when(mMockPackageMonitor).unregister(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor, never()).register(any(), any(), any()); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void stop_updateEnabled_started_beforeDelay() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + doThrow(new IllegalStateException("NOT REGISTERED")).when(mMockPackageMonitor).unregister(); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.dispatchAll(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor, never()).register(any(), any(), any()); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void stop_updateEnabled_started_afterDelay() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verify(mMockContentCaptureManagerService).createRemoteContentProtectionService(); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void stop_updateDisabled_notStarted() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + doThrow(new IllegalStateException("NOT REGISTERED")).when(mMockPackageMonitor).unregister(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor, never()).register(any(), any(), any()); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void stop_updateDisabled_started_beforeDelay() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + doThrow(new IllegalStateException("NOT REGISTERED")).when(mMockPackageMonitor).unregister(); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.dispatchAll(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor, never()).register(any(), any(), any()); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void stop_updateDisabled_started_afterDelay() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + mContentProtectionAllowlistManager.stop(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void start_afterStop_beforeDelay() { + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.dispatchAll(); + mContentProtectionAllowlistManager.stop(); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor).unregister(); + } + + @Test + public void start_afterStop_afterDelay() { + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + mContentProtectionAllowlistManager.stop(); + + mContentProtectionAllowlistManager.start(DELAY_MS); + mTestLooper.moveTimeForward(DELAY_MS); + mTestLooper.dispatchNext(); + + assertThat(mHandler.hasMessagesOrCallbacks()).isFalse(); + verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler)); + verify(mMockPackageMonitor).unregister(); } @Test @@ -54,5 +297,86 @@ public class ContentProtectionAllowlistManagerTest { boolean actual = mContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME); assertThat(actual).isFalse(); + verifyZeroInteractions(mMockContentCaptureManagerService); + verifyZeroInteractions(mMockPackageMonitor); + } + + @Test + public void handleUpdate_updateDisabled() { + mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + ContentProtectionAllowlistManager manager = + new ContentProtectionAllowlistManager( + mMockContentCaptureManagerService, mHandler, TIMEOUT_MS); + + manager.mPackageMonitor.onSomePackagesChanged(); + + verifyZeroInteractions(mMockContentCaptureManagerService); + } + + @Test + public void handleUpdate_updateEnabled() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + ContentProtectionAllowlistManager manager = + new ContentProtectionAllowlistManager( + mMockContentCaptureManagerService, mHandler, TIMEOUT_MS); + + manager.mPackageMonitor.onSomePackagesChanged(); + + verify(mMockContentCaptureManagerService).createRemoteContentProtectionService(); + } + + @Test + public void handleUpdate_rateLimit_noService() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + ContentProtectionAllowlistManager manager = + new ContentProtectionAllowlistManager( + mMockContentCaptureManagerService, mHandler, TIMEOUT_MS); + + manager.mPackageMonitor.onSomePackagesChanged(); + manager.mPackageMonitor.onSomePackagesChanged(); + + verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService(); + } + + @Test + public void handleUpdate_rateLimit_beforeTimeout() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + ContentProtectionAllowlistManager manager = + new ContentProtectionAllowlistManager( + mMockContentCaptureManagerService, mHandler, TIMEOUT_MS); + when(mMockContentCaptureManagerService.createRemoteContentProtectionService()) + .thenReturn(mMockRemoteContentProtectionService); + + manager.mPackageMonitor.onSomePackagesChanged(); + manager.mPackageMonitor.onSomePackagesChanged(); + + verify(mMockContentCaptureManagerService).createRemoteContentProtectionService(); + } + + @Test + public void handleUpdate_rateLimit_afterTimeout() { + mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED); + ContentProtectionAllowlistManager manager = + new ContentProtectionAllowlistManager( + mMockContentCaptureManagerService, mHandler, /* timeoutMs= */ 0L); + when(mMockContentCaptureManagerService.createRemoteContentProtectionService()) + .thenReturn(mMockRemoteContentProtectionService); + + manager.mPackageMonitor.onSomePackagesChanged(); + manager.mPackageMonitor.onSomePackagesChanged(); + + verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService(); + } + + private class TestContentProtectionAllowlistManager extends ContentProtectionAllowlistManager { + + TestContentProtectionAllowlistManager() { + super(mMockContentCaptureManagerService, mHandler, TIMEOUT_MS); + } + + @Override + protected PackageMonitor createPackageMonitor() { + return mMockPackageMonitor; + } } } diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java index 0e80bfdae291..5fe60d779fa6 100644 --- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java @@ -55,9 +55,15 @@ public class ContentProtectionConsentManagerTest { private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent"; + private static final String KEY_CONTENT_PROTECTION_USER_CONSENT = + "content_protection_user_consent"; + private static final Uri URI_PACKAGE_VERIFIER_USER_CONSENT = Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT); + private static final Uri URI_CONTENT_PROTECTION_USER_CONSENT = + Settings.Global.getUriFor(KEY_CONTENT_PROTECTION_USER_CONSENT); + private static final int VALUE_TRUE = 1; private static final int VALUE_FALSE = -1; @@ -96,7 +102,18 @@ public class ContentProtectionConsentManagerTest { @Test public void isConsentGranted_packageVerifierNotGranted() { ContentProtectionConsentManager manager = - createContentProtectionConsentManager(VALUE_FALSE); + createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE); + + boolean actual = manager.isConsentGranted(TEST_USER_ID); + + assertThat(actual).isFalse(); + verifyZeroInteractions(mMockDevicePolicyManagerInternal); + } + + @Test + public void isConsentGranted_contentProtectionNotGranted() { + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE); boolean actual = manager.isConsentGranted(TEST_USER_ID); @@ -106,7 +123,8 @@ public class ContentProtectionConsentManagerTest { @Test public void isConsentGranted_packageVerifierGranted_userNotManaged() { - ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE); + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE); boolean actual = manager.isConsentGranted(TEST_USER_ID); @@ -118,7 +136,8 @@ public class ContentProtectionConsentManagerTest { public void isConsentGranted_packageVerifierGranted_userManaged() { when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID)) .thenReturn(true); - ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE); + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE); boolean actual = manager.isConsentGranted(TEST_USER_ID); @@ -128,7 +147,7 @@ public class ContentProtectionConsentManagerTest { @Test public void isConsentGranted_packageVerifierDefault() { ContentProtectionConsentManager manager = - createContentProtectionConsentManager(VALUE_DEFAULT); + createContentProtectionConsentManager(VALUE_DEFAULT, VALUE_TRUE); boolean actual = manager.isConsentGranted(TEST_USER_ID); @@ -137,15 +156,27 @@ public class ContentProtectionConsentManagerTest { } @Test - public void contentObserver() throws Exception { - ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE); + public void isConsentGranted_contentProtectionDefault() { + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT); + + boolean actual = manager.isConsentGranted(TEST_USER_ID); + + assertThat(actual).isTrue(); + verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID); + } + + @Test + public void contentObserver_packageVerifier() { + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT); boolean firstActual = manager.isConsentGranted(TEST_USER_ID); - Settings.Global.putInt( - mTestableContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_FALSE); - // Observer has to be called manually, mTestableContentResolver is not propagating - manager.mContentObserver.onChange( - /* selfChange= */ false, URI_PACKAGE_VERIFIER_USER_CONSENT, TEST_USER_ID); + notifyContentObserver( + manager, + URI_PACKAGE_VERIFIER_USER_CONSENT, + KEY_PACKAGE_VERIFIER_USER_CONSENT, + VALUE_FALSE); boolean secondActual = manager.isConsentGranted(TEST_USER_ID); assertThat(firstActual).isTrue(); @@ -153,6 +184,31 @@ public class ContentProtectionConsentManagerTest { verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID); } + @Test + public void contentObserver_contentProtection() { + ContentProtectionConsentManager manager = + createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT); + boolean firstActual = manager.isConsentGranted(TEST_USER_ID); + + notifyContentObserver( + manager, + URI_CONTENT_PROTECTION_USER_CONSENT, + KEY_CONTENT_PROTECTION_USER_CONSENT, + VALUE_FALSE); + boolean secondActual = manager.isConsentGranted(TEST_USER_ID); + + assertThat(firstActual).isTrue(); + assertThat(secondActual).isFalse(); + verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID); + } + + private void notifyContentObserver( + ContentProtectionConsentManager manager, Uri uri, String key, int value) { + Settings.Global.putInt(mTestableContentResolver, key, value); + // Observer has to be called manually, mTestableContentResolver is not propagating + manager.mContentObserver.onChange(/* selfChange= */ false, uri, TEST_USER_ID); + } + private ContentProtectionConsentManager createContentProtectionConsentManager( ContentResolver contentResolver) { return new ContentProtectionConsentManager( @@ -162,11 +218,15 @@ public class ContentProtectionConsentManagerTest { } private ContentProtectionConsentManager createContentProtectionConsentManager( - int valuePackageVerifierUserConsent) { + int valuePackageVerifierUserConsent, int valueContentProtectionUserConsent) { Settings.Global.putInt( mTestableContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, valuePackageVerifierUserConsent); + Settings.Global.putInt( + mTestableContentResolver, + KEY_CONTENT_PROTECTION_USER_CONSENT, + valueContentProtectionUserConsent); return createContentProtectionConsentManager(mTestableContentResolver); } } |