From 424a88c7a2b895253526890ddc68dc26a883bb35 Mon Sep 17 00:00:00 2001 From: Nino Jagar Date: Thu, 18 May 2023 16:38:43 +0000 Subject: [PATCH] Add flags and options for content protection BYPASS_INCLUSIVE_LANGUAGE_REASON=All issues are in the existing code which is already in production. New code has no issues. Bug: 275732576 Test: Added new tests Change-Id: I1273107f2d925586271da2638d36094a1ab84c60 --- .../content/ContentCaptureOptions.java | 225 +++++++++++++++--- .../contentcapture/ContentCaptureManager.java | 39 ++- .../content/ContentCaptureOptionsTest.java | 94 +++++++- .../ContentCaptureManagerService.java | 209 +++++++++++----- .../ContentCaptureManagerServiceTest.java | 114 +++++++++ 5 files changed, 578 insertions(+), 103 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 856bde870bcfe..36e0529e35662 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -75,6 +75,20 @@ public final class ContentCaptureOptions implements Parcelable { */ public final boolean disableFlushForViewTreeAppearing; + /** + * Is the content capture receiver enabled. + * + * @hide + */ + public final boolean enableReceiver; + + /** + * Options for the content protection flow. + * + * @hide + */ + @NonNull public final ContentProtectionOptions contentProtectionOptions; + /** * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). @@ -94,52 +108,99 @@ public final class ContentCaptureOptions implements Parcelable { * for contexts belonging to the content capture service app. */ public ContentCaptureOptions(int loggingLevel) { - this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, - /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, - /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, + this( + /* lite= */ true, + loggingLevel, + /* maxBufferSize= */ 0, + /* idleFlushingFrequencyMs= */ 0, + /* textChangeFlushingFrequencyMs= */ 0, + /* logHistorySize= */ 0, + /* disableFlushForViewTreeAppearing= */ false, + /* enableReceiver= */ false, + new ContentProtectionOptions( + /* enableReceiver= */ false, + /* bufferSize= */ 0), /* whitelistedComponents= */ null); } - /** - * Default constructor. - */ - public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, - int textChangeFlushingFrequencyMs, int logHistorySize, - @SuppressLint({"ConcreteCollection", "NullableCollection"}) - @Nullable ArraySet whitelistedComponents) { - this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, + /** Default constructor. */ + public ContentCaptureOptions( + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet whitelistedComponents) { + this( + /* lite= */ false, + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, + new ContentProtectionOptions( + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), whitelistedComponents); } /** @hide */ - public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, - int textChangeFlushingFrequencyMs, int logHistorySize, + public ContentCaptureOptions( + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, boolean disableFlushForViewTreeAppearing, - @SuppressLint({"ConcreteCollection", "NullableCollection"}) - @Nullable ArraySet whitelistedComponents) { - this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, + boolean enableReceiver, + @NonNull ContentProtectionOptions contentProtectionOptions, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet whitelistedComponents) { + this( + /* lite= */ false, + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, + disableFlushForViewTreeAppearing, + enableReceiver, + contentProtectionOptions, whitelistedComponents); } /** @hide */ @VisibleForTesting public ContentCaptureOptions(@Nullable ArraySet whitelistedComponents) { - this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE, + this( + ContentCaptureManager.LOGGING_LEVEL_VERBOSE, ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, + new ContentProtectionOptions( + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), whitelistedComponents); } - private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, - int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, + private ContentCaptureOptions( + boolean lite, + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, boolean disableFlushForViewTreeAppearing, - @Nullable ArraySet whitelistedComponents) { + boolean enableReceiver, + @NonNull ContentProtectionOptions contentProtectionOptions, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet whitelistedComponents) { this.lite = lite; this.loggingLevel = loggingLevel; this.maxBufferSize = maxBufferSize; @@ -147,6 +208,8 @@ public final class ContentCaptureOptions implements Parcelable { this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; this.logHistorySize = logHistorySize; this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; + this.enableReceiver = enableReceiver; + this.contentProtectionOptions = contentProtectionOptions; this.whitelistedComponents = whitelistedComponents; } @@ -191,12 +254,22 @@ public final class ContentCaptureOptions implements Parcelable { return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]"; } final StringBuilder string = new StringBuilder("ContentCaptureOptions ["); - string.append("loggingLevel=").append(loggingLevel) - .append(", maxBufferSize=").append(maxBufferSize) - .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) - .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) - .append(", logHistorySize=").append(logHistorySize) - .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); + string.append("loggingLevel=") + .append(loggingLevel) + .append(", maxBufferSize=") + .append(maxBufferSize) + .append(", idleFlushingFrequencyMs=") + .append(idleFlushingFrequencyMs) + .append(", textChangeFlushingFrequencyMs=") + .append(textChangeFlushingFrequencyMs) + .append(", logHistorySize=") + .append(logHistorySize) + .append(", disableFlushForViewTreeAppearing=") + .append(disableFlushForViewTreeAppearing) + .append(", enableReceiver=") + .append(enableReceiver) + .append(", contentProtectionOptions=") + .append(contentProtectionOptions); if (whitelistedComponents != null) { string.append(", whitelisted=").append(whitelistedComponents); } @@ -210,11 +283,21 @@ public final class ContentCaptureOptions implements Parcelable { pw.print(", lite"); return; } - pw.print(", bufferSize="); pw.print(maxBufferSize); - pw.print(", idle="); pw.print(idleFlushingFrequencyMs); - pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); - pw.print(", logSize="); pw.print(logHistorySize); - pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); + pw.print(", bufferSize="); + pw.print(maxBufferSize); + pw.print(", idle="); + pw.print(idleFlushingFrequencyMs); + pw.print(", textIdle="); + pw.print(textChangeFlushingFrequencyMs); + pw.print(", logSize="); + pw.print(logHistorySize); + pw.print(", disableFlushForViewTreeAppearing="); + pw.print(disableFlushForViewTreeAppearing); + pw.print(", enableReceiver="); + pw.print(enableReceiver); + pw.print(", contentProtectionOptions=["); + contentProtectionOptions.dumpShort(pw); + pw.print("]"); if (whitelistedComponents != null) { pw.print(", whitelisted="); pw.print(whitelistedComponents); } @@ -236,6 +319,8 @@ public final class ContentCaptureOptions implements Parcelable { parcel.writeInt(textChangeFlushingFrequencyMs); parcel.writeInt(logHistorySize); parcel.writeBoolean(disableFlushForViewTreeAppearing); + parcel.writeBoolean(enableReceiver); + contentProtectionOptions.writeToParcel(parcel); parcel.writeArraySet(whitelistedComponents); } @@ -254,12 +339,22 @@ public final class ContentCaptureOptions implements Parcelable { final int textChangeFlushingFrequencyMs = parcel.readInt(); final int logHistorySize = parcel.readInt(); final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); + final boolean enableReceiver = parcel.readBoolean(); + final ContentProtectionOptions contentProtectionOptions = + ContentProtectionOptions.createFromParcel(parcel); @SuppressWarnings("unchecked") final ArraySet whitelistedComponents = (ArraySet) parcel.readArraySet(null); - return new ContentCaptureOptions(loggingLevel, maxBufferSize, - idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, - disableFlushForViewTreeAppearing, whitelistedComponents); + return new ContentCaptureOptions( + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, + disableFlushForViewTreeAppearing, + enableReceiver, + contentProtectionOptions, + whitelistedComponents); } @Override @@ -267,4 +362,62 @@ public final class ContentCaptureOptions implements Parcelable { return new ContentCaptureOptions[size]; } }; + + /** + * Content protection options for a given package. + * + *

Does not implement {@code Parcelable} since it is an inner class without a matching AIDL. + * + * @hide + */ + public static class ContentProtectionOptions { + + /** + * Is the content protection receiver enabled. + * + * @hide + */ + public final boolean enableReceiver; + + /** + * Size of the in-memory ring buffer for the content protection flow. + * + * @hide + */ + public final int bufferSize; + + public ContentProtectionOptions(boolean enableReceiver, int bufferSize) { + this.enableReceiver = enableReceiver; + this.bufferSize = bufferSize; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("ContentProtectionOptions ["); + stringBuilder + .append("enableReceiver=") + .append(enableReceiver) + .append(", bufferSize=") + .append(bufferSize); + return stringBuilder.append(']').toString(); + } + + private void dumpShort(@NonNull PrintWriter pw) { + pw.print("enableReceiver="); + pw.print(enableReceiver); + pw.print(", bufferSize="); + pw.print(bufferSize); + } + + private void writeToParcel(Parcel parcel) { + parcel.writeBoolean(enableReceiver); + parcel.writeInt(bufferSize); + } + + private static ContentProtectionOptions createFromParcel(Parcel parcel) { + boolean enableReceiver = parcel.readBoolean(); + int bufferSize = parcel.readInt(); + return new ContentProtectionOptions(enableReceiver, bufferSize); + } + } } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 668351b949c1b..c37e311209df1 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -352,6 +352,30 @@ public final class ContentCaptureManager { public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = "disable_flush_for_view_tree_appearing"; + /** + * Enables the content protection receiver. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER = + "enable_content_protection_receiver"; + + /** + * Sets the size of the app blocklist for the content protection flow. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = + "content_protection_apps_blocklist_size"; + + /** + * Sets the size of the in-memory ring buffer for the content protection flow. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE = + "content_protection_buffer_size"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -384,6 +408,14 @@ public final class ContentCaptureManager { public static final int DEFAULT_LOG_HISTORY_SIZE = 10; /** @hide */ public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false; + /** @hide */ + public static final boolean DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER = true; + /** @hide */ + public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; + /** @hide */ + public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000; + /** @hide */ + public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; private final Object mLock = new Object(); @@ -425,11 +457,11 @@ public final class ContentCaptureManager { /** @hide */ static class StrippedContext { - final String mPackageName; - final String mContext; + @NonNull final String mPackageName; + @NonNull final String mContext; final @UserIdInt int mUserId; - private StrippedContext(Context context) { + private StrippedContext(@NonNull Context context) { mPackageName = context.getPackageName(); mContext = context.toString(); mUserId = context.getUserId(); @@ -440,6 +472,7 @@ public final class ContentCaptureManager { return mContext; } + @NonNull public String getPackageName() { return mPackageName; } diff --git a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java index c6f4fa27dc834..f8348d28b7fe4 100644 --- a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java +++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Parcel; import android.util.ArraySet; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; @@ -40,16 +41,29 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ContentCaptureOptionsTest { - private final ComponentName mContextComponent = new ComponentName("marco", "polo"); - private final ComponentName mComp1 = new ComponentName("comp", "one"); - private final ComponentName mComp2 = new ComponentName("two", "comp"); + private static final ComponentName CONTEXT_COMPONENT = new ComponentName("marco", "polo"); + private static final ComponentName COMPONENT1 = new ComponentName("comp", "one"); + private static final ComponentName COMPONENT2 = new ComponentName("two", "comp"); + private static final ContentCaptureOptions CONTENT_CAPTURE_OPTIONS = + new ContentCaptureOptions( + /* loggingLevel= */ 1000, + /* maxBufferSize= */ 1001, + /* idleFlushingFrequencyMs= */ 1002, + /* textChangeFlushingFrequencyMs= */ 1003, + /* logHistorySize= */ 1004, + /* disableFlushForViewTreeAppearing= */ true, + /* enableReceiver= */ false, + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ true, + /* bufferSize= */ 2001), + /* whitelistedComponents= */ toSet(COMPONENT1, COMPONENT2)); @Mock private Context mContext; @Mock private ContentCaptureClient mClient; @Before public void setExpectation() { - when(mClient.contentCaptureClientGetComponentName()).thenReturn(mContextComponent); + when(mClient.contentCaptureClientGetComponentName()).thenReturn(CONTEXT_COMPONENT); when(mContext.getContentCaptureClient()).thenReturn(mClient); } @@ -67,26 +81,27 @@ public class ContentCaptureOptionsTest { @Test public void testIsWhitelisted_notWhitelisted() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mComp2)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(COMPONENT1, COMPONENT2)); assertThat(options.isWhitelisted(mContext)).isFalse(); } @Test public void testIsWhitelisted_whitelisted() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mContextComponent)); + ContentCaptureOptions options = + new ContentCaptureOptions(toSet(COMPONENT1, CONTEXT_COMPONENT)); assertThat(options.isWhitelisted(mContext)).isTrue(); } @Test public void testIsWhitelisted_invalidContext() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT)); Context invalidContext = mock(Context.class); // has no client assertThat(options.isWhitelisted(invalidContext)).isFalse(); } @Test public void testIsWhitelisted_clientWithNullComponentName() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT)); ContentCaptureClient client = mock(ContentCaptureClient.class); Context context = mock(Context.class); when(context.getContentCaptureClient()).thenReturn(client); @@ -94,8 +109,69 @@ public class ContentCaptureOptionsTest { assertThat(options.isWhitelisted(context)).isFalse(); } + @Test + public void testToString() { + String actual = CONTENT_CAPTURE_OPTIONS.toString(); + + String expected = + new StringBuilder("ContentCaptureOptions [") + .append("loggingLevel=") + .append(CONTENT_CAPTURE_OPTIONS.loggingLevel) + .append(", maxBufferSize=") + .append(CONTENT_CAPTURE_OPTIONS.maxBufferSize) + .append(", idleFlushingFrequencyMs=") + .append(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs) + .append(", textChangeFlushingFrequencyMs=") + .append(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs) + .append(", logHistorySize=") + .append(CONTENT_CAPTURE_OPTIONS.logHistorySize) + .append(", disableFlushForViewTreeAppearing=") + .append(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing) + .append(", enableReceiver=") + .append(CONTENT_CAPTURE_OPTIONS.enableReceiver) + .append(", contentProtectionOptions=ContentProtectionOptions [") + .append("enableReceiver=") + .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver) + .append(", bufferSize=") + .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize) + .append("], whitelisted=") + .append(CONTENT_CAPTURE_OPTIONS.whitelistedComponents) + .append(']') + .toString(); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testParcelSerializationDeserialization() { + Parcel parcel = Parcel.obtain(); + CONTENT_CAPTURE_OPTIONS.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + ContentCaptureOptions actual = ContentCaptureOptions.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + assertThat(actual).isNotNull(); + assertThat(actual.loggingLevel).isEqualTo(CONTENT_CAPTURE_OPTIONS.loggingLevel); + assertThat(actual.maxBufferSize).isEqualTo(CONTENT_CAPTURE_OPTIONS.maxBufferSize); + assertThat(actual.idleFlushingFrequencyMs) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs); + assertThat(actual.textChangeFlushingFrequencyMs) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs); + assertThat(actual.logHistorySize).isEqualTo(CONTENT_CAPTURE_OPTIONS.logHistorySize); + assertThat(actual.disableFlushForViewTreeAppearing) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing); + assertThat(actual.enableReceiver).isEqualTo(CONTENT_CAPTURE_OPTIONS.enableReceiver); + assertThat(actual.contentProtectionOptions).isNotNull(); + assertThat(actual.contentProtectionOptions.enableReceiver) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver); + assertThat(actual.contentProtectionOptions.bufferSize) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize); + assertThat(actual.whitelistedComponents) + .containsExactlyElementsIn(CONTENT_CAPTURE_OPTIONS.whitelistedComponents); + } + @NonNull - private ArraySet toSet(@Nullable ComponentName... comps) { + private static ArraySet toSet(@Nullable ComponentName... comps) { ArraySet set = new ArraySet<>(); if (comps != null) { for (int i = 0; i < comps.length; i++) { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 61fc32d5fa156..51359add8fd18 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -163,13 +163,35 @@ public final class ContentCaptureManagerService extends private boolean mDisabledByDeviceConfig; // Device-config settings that are cached and passed back to apps - @GuardedBy("mLock") int mDevCfgLoggingLevel; - @GuardedBy("mLock") int mDevCfgMaxBufferSize; - @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs; - @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs; - @GuardedBy("mLock") int mDevCfgLogHistorySize; - @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs; - @GuardedBy("mLock") boolean mDevCfgDisableFlushForViewTreeAppearing; + @GuardedBy("mLock") + int mDevCfgLoggingLevel; + + @GuardedBy("mLock") + int mDevCfgMaxBufferSize; + + @GuardedBy("mLock") + int mDevCfgIdleFlushingFrequencyMs; + + @GuardedBy("mLock") + int mDevCfgTextChangeFlushingFrequencyMs; + + @GuardedBy("mLock") + int mDevCfgLogHistorySize; + + @GuardedBy("mLock") + int mDevCfgIdleUnbindTimeoutMs; + + @GuardedBy("mLock") + boolean mDevCfgDisableFlushForViewTreeAppearing; + + @GuardedBy("mLock") + boolean mDevCfgEnableContentProtectionReceiver; + + @GuardedBy("mLock") + int mDevCfgContentProtectionAppsBlocklistSize; + + @GuardedBy("mLock") + int mDevCfgContentProtectionBufferSize; private final Executor mDataShareExecutor = Executors.newCachedThreadPool(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -362,6 +384,11 @@ public final class ContentCaptureManagerService extends case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: case ContentCaptureManager .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE: setFineTuneParamsFromDeviceConfig(); return; default: @@ -372,39 +399,78 @@ public final class ContentCaptureManagerService extends private void setFineTuneParamsFromDeviceConfig() { synchronized (mLock) { - mDevCfgMaxBufferSize = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, - ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE); - mDevCfgIdleFlushingFrequencyMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, - ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS); - mDevCfgTextChangeFlushingFrequencyMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY, - ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS); - mDevCfgLogHistorySize = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); - mDevCfgIdleUnbindTimeoutMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, - (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); - mDevCfgDisableFlushForViewTreeAppearing = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager - .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, - false); + mDevCfgMaxBufferSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE); + mDevCfgIdleFlushingFrequencyMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS); + mDevCfgTextChangeFlushingFrequencyMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS); + mDevCfgLogHistorySize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, + 20); + mDevCfgIdleUnbindTimeoutMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, + (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + mDevCfgDisableFlushForViewTreeAppearing = + DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + 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); + mDevCfgContentProtectionAppsBlocklistSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE); + mDevCfgContentProtectionBufferSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE); if (verbose) { - Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): " - + "bufferSize=" + mDevCfgMaxBufferSize - + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs - + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs - + ", logHistory=" + mDevCfgLogHistorySize - + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs - + ", disableFlushForViewTreeAppearing=" - + mDevCfgDisableFlushForViewTreeAppearing); + Slog.v( + TAG, + "setFineTuneParamsFromDeviceConfig(): " + + "bufferSize=" + + mDevCfgMaxBufferSize + + ", idleFlush=" + + mDevCfgIdleFlushingFrequencyMs + + ", textFluxh=" + + mDevCfgTextChangeFlushingFrequencyMs + + ", logHistory=" + + mDevCfgLogHistorySize + + ", idleUnbindTimeoutMs=" + + mDevCfgIdleUnbindTimeoutMs + + ", disableFlushForViewTreeAppearing=" + + mDevCfgDisableFlushForViewTreeAppearing + + ", enableContentProtectionReceiver=" + + mDevCfgEnableContentProtectionReceiver + + ", contentProtectionAppsBlocklistSize=" + + mDevCfgContentProtectionAppsBlocklistSize + + ", contentProtectionBufferSize=" + + mDevCfgContentProtectionBufferSize); } } } @@ -645,21 +711,46 @@ public final class ContentCaptureManagerService extends final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("Users disabled by Settings: "); pw.println(mDisabledBySettings); - pw.print(prefix); pw.println("DeviceConfig Settings: "); - pw.print(prefix2); pw.print("disabled: "); pw.println(mDisabledByDeviceConfig); - pw.print(prefix2); pw.print("loggingLevel: "); pw.println(mDevCfgLoggingLevel); - pw.print(prefix2); pw.print("maxBufferSize: "); pw.println(mDevCfgMaxBufferSize); - pw.print(prefix2); pw.print("idleFlushingFrequencyMs: "); + pw.print(prefix); + pw.print("Users disabled by Settings: "); + pw.println(mDisabledBySettings); + pw.print(prefix); + pw.println("DeviceConfig Settings: "); + pw.print(prefix2); + pw.print("disabled: "); + pw.println(mDisabledByDeviceConfig); + pw.print(prefix2); + pw.print("loggingLevel: "); + pw.println(mDevCfgLoggingLevel); + pw.print(prefix2); + pw.print("maxBufferSize: "); + pw.println(mDevCfgMaxBufferSize); + pw.print(prefix2); + pw.print("idleFlushingFrequencyMs: "); pw.println(mDevCfgIdleFlushingFrequencyMs); - pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: "); + pw.print(prefix2); + pw.print("textChangeFlushingFrequencyMs: "); pw.println(mDevCfgTextChangeFlushingFrequencyMs); - pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize); - pw.print(prefix2); pw.print("idleUnbindTimeoutMs: "); + pw.print(prefix2); + pw.print("logHistorySize: "); + pw.println(mDevCfgLogHistorySize); + pw.print(prefix2); + pw.print("idleUnbindTimeoutMs: "); pw.println(mDevCfgIdleUnbindTimeoutMs); - pw.print(prefix2); pw.print("disableFlushForViewTreeAppearing: "); + pw.print(prefix2); + pw.print("disableFlushForViewTreeAppearing: "); pw.println(mDevCfgDisableFlushForViewTreeAppearing); - pw.print(prefix); pw.println("Global Options:"); + pw.print(prefix2); + pw.print("enableContentProtectionReceiver: "); + pw.println(mDevCfgEnableContentProtectionReceiver); + pw.print(prefix2); + pw.print("contentProtectionAppsBlocklistSize: "); + pw.println(mDevCfgContentProtectionAppsBlocklistSize); + pw.print(prefix2); + pw.print("contentProtectionBufferSize: "); + pw.println(mDevCfgContentProtectionBufferSize); + pw.print(prefix); + pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); } @@ -1019,11 +1110,19 @@ public final class ContentCaptureManagerService extends } synchronized (mLock) { - final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, - mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, - mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, - mDevCfgDisableFlushForViewTreeAppearing, - whitelistedComponents); + final ContentCaptureOptions options = + new ContentCaptureOptions( + mDevCfgLoggingLevel, + mDevCfgMaxBufferSize, + mDevCfgIdleFlushingFrequencyMs, + mDevCfgTextChangeFlushingFrequencyMs, + mDevCfgLogHistorySize, + mDevCfgDisableFlushForViewTreeAppearing, + /* enableReceiver= */ true, + new ContentCaptureOptions.ContentProtectionOptions( + mDevCfgEnableContentProtectionReceiver, + mDevCfgContentProtectionBufferSize), + whitelistedComponents); if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); return options; } diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java new file mode 100644 index 0000000000000..c872a11e7988e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 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. + */ + +package com.android.server.contentcapture; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.ContentCaptureOptions; +import android.content.Context; +import android.content.pm.UserInfo; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; + +import com.google.common.collect.ImmutableList; + +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; + +/** + * Test for {@link ContentCaptureManagerService}. + * + *

Run with: {@code atest + * FrameworksServicesTests:com.android.server.contentcapture.ContentCaptureManagerServiceTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@SuppressWarnings("GuardedBy") // Service not really running, no need to expose locks +public class ContentCaptureManagerServiceTest { + + private static final int USER_ID = 1234; + + private static final String PACKAGE_NAME = "com.test.package"; + + private static final Context sContext = ApplicationProvider.getApplicationContext(); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private UserManagerInternal mMockUserManagerInternal; + + private ContentCaptureManagerService mContentCaptureManagerService; + + @Before + public void setup() { + when(mMockUserManagerInternal.getUserInfos()).thenReturn(new UserInfo[0]); + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal); + mContentCaptureManagerService = new ContentCaptureManagerService(sContext); + } + + @Test + public void getOptions_notAllowlisted() { + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNull(); + } + + @Test + public void getOptions_allowlisted_contentCaptureReceiver() { + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNotNull(); + assertThat(actual.enableReceiver).isTrue(); + assertThat(actual.contentProtectionOptions.enableReceiver).isFalse(); + assertThat(actual.whitelistedComponents).isNull(); + } + + @Test + public void getOptions_allowlisted_bothReceivers() { + mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNotNull(); + assertThat(actual.enableReceiver).isTrue(); + assertThat(actual.contentProtectionOptions.enableReceiver).isTrue(); + assertThat(actual.whitelistedComponents).isNull(); + } +}