diff options
| author | 2023-10-27 22:44:09 +0000 | |
|---|---|---|
| committer | 2023-10-27 22:44:09 +0000 | |
| commit | 14e1cffe68dee94f5c03aceabb0291cc4d52e085 (patch) | |
| tree | 3bc0a6f4556b9b03ee0675092c04c72cd08a4ab6 | |
| parent | e619e972988d960bad876ded08ec1d222fb740da (diff) | |
Skeleton for content protection allowlist manager
Bug: 302188278
Test: Unit tests and end-to-end from a larger commit
Change-Id: I1767b91204ebe76486f7f15c125110943a924ebe
5 files changed, 729 insertions, 111 deletions
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/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/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; + } } } |