Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 424a88c7 authored by Nino Jagar's avatar Nino Jagar
Browse files

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
parent 97a0ec41
Loading
Loading
Loading
Loading
+189 −36
Original line number Diff line number Diff line
@@ -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<ComponentName> 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<ComponentName> 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<ComponentName> whitelistedComponents) {
        this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
                textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing,
            boolean enableReceiver,
            @NonNull ContentProtectionOptions contentProtectionOptions,
            @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable
                    ArraySet<ComponentName> whitelistedComponents) {
        this(
                /* lite= */ false,
                loggingLevel,
                maxBufferSize,
                idleFlushingFrequencyMs,
                textChangeFlushingFrequencyMs,
                logHistorySize,
                disableFlushForViewTreeAppearing,
                enableReceiver,
                contentProtectionOptions,
                whitelistedComponents);
    }

    /** @hide */
    @VisibleForTesting
    public ContentCaptureOptions(@Nullable ArraySet<ComponentName> 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<ComponentName> whitelistedComponents) {
            boolean enableReceiver,
            @NonNull ContentProtectionOptions contentProtectionOptions,
            @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable
                    ArraySet<ComponentName> 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<ComponentName> whitelistedComponents =
                            (ArraySet<ComponentName>) 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.
     *
     * <p>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);
        }
    }
}
+36 −3
Original line number Diff line number Diff line
@@ -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;
        }
+85 −9
Original line number Diff line number Diff line
@@ -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<ComponentName> toSet(@Nullable ComponentName... comps) {
    private static ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) {
        ArraySet<ComponentName> set = new ArraySet<>();
        if (comps != null) {
            for (int i = 0; i < comps.length; i++) {
+154 −55

File changed.

Preview size limit exceeded, changes collapsed.

+114 −0

File added.

Preview size limit exceeded, changes collapsed.