Loading core/java/android/view/contentcapture/MainContentCaptureSession.java +14 −5 Original line number Diff line number Diff line Loading @@ -317,16 +317,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } // Should not be possible for mComponentName to be null here but check anyway if (mManager.mOptions.contentProtectionOptions.enableReceiver && mManager.getContentProtectionEventBuffer() != null && mComponentName != null) { if (isContentProtectionEnabled()) { mContentProtectionEventProcessor = new ContentProtectionEventProcessor( mManager.getContentProtectionEventBuffer(), mHandler, mSystemServerInterface, mComponentName.getPackageName()); mComponentName.getPackageName(), mManager.mOptions.contentProtectionOptions); } else { mContentProtectionEventProcessor = null; } Loading Loading @@ -956,4 +954,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private boolean isContentCaptureReceiverEnabled() { return mManager.mOptions.enableReceiver; } @UiThread private boolean isContentProtectionEnabled() { // Should not be possible for mComponentName to be null here but check anyway // Should not be possible for groups to be empty if receiver is enabled but check anyway return mManager.mOptions.contentProtectionOptions.enableReceiver && mManager.getContentProtectionEventBuffer() != null && mComponentName != null && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty() || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty()); } } core/java/android/view/contentprotection/ContentProtectionEventProcessor.java +64 −90 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package android.view.contentprotection; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; import android.content.ContentCaptureOptions; import android.content.pm.ParceledListSlice; import android.os.Handler; import android.text.InputType; import android.util.Log; import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.IContentCaptureManager; Loading @@ -33,10 +33,10 @@ import com.android.internal.util.RingBuffer; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Stream; /** * Main entry point for processing {@link ContentCaptureEvent} for the content protection flow. Loading @@ -47,33 +47,13 @@ public class ContentProtectionEventProcessor { private static final String TAG = "ContentProtectionEventProcessor"; private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES = Collections.unmodifiableList( Arrays.asList( InputType.TYPE_NUMBER_VARIATION_PASSWORD, InputType.TYPE_TEXT_VARIATION_PASSWORD, InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)); private static final List<String> PASSWORD_TEXTS = Collections.unmodifiableList( Arrays.asList("password", "pass word", "code", "pin", "credential")); private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS = Collections.unmodifiableList( Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in")); private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3); private static final String ANDROID_CLASS_NAME_PREFIX = "android."; private static final Set<Integer> EVENT_TYPES_TO_STORE = Collections.unmodifiableSet( new HashSet<>( Arrays.asList( Set.of( ContentCaptureEvent.TYPE_VIEW_APPEARED, ContentCaptureEvent.TYPE_VIEW_DISAPPEARED, ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED))); ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED); private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; Loading @@ -85,11 +65,7 @@ public class ContentProtectionEventProcessor { @NonNull private final String mPackageName; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public boolean mPasswordFieldDetected = false; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public boolean mSuspiciousTextDetected = false; @NonNull private final ContentCaptureOptions.ContentProtectionOptions mOptions; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @Nullable Loading @@ -97,15 +73,32 @@ public class ContentProtectionEventProcessor { private int mResetLoginRemainingEventsToProcess; private boolean mAnyGroupFound = false; // Ordered by priority private final List<SearchGroup> mGroupsRequired; // Ordered by priority private final List<SearchGroup> mGroupsOptional; // Ordered by priority private final List<SearchGroup> mGroupsAll; public ContentProtectionEventProcessor( @NonNull RingBuffer<ContentCaptureEvent> eventBuffer, @NonNull Handler handler, @NonNull IContentCaptureManager contentCaptureManager, @NonNull String packageName) { @NonNull String packageName, @NonNull ContentCaptureOptions.ContentProtectionOptions options) { mEventBuffer = eventBuffer; mHandler = handler; mContentCaptureManager = contentCaptureManager; mPackageName = packageName; mOptions = options; mGroupsRequired = options.requiredGroups.stream().map(SearchGroup::new).toList(); mGroupsOptional = options.optionalGroups.stream().map(SearchGroup::new).toList(); mGroupsAll = Stream.of(mGroupsRequired, mGroupsOptional).flatMap(Collection::stream).toList(); } /** Main entry point for {@link ContentCaptureEvent} processing. */ Loading @@ -130,9 +123,31 @@ public class ContentProtectionEventProcessor { @UiThread private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) { mPasswordFieldDetected |= isPasswordField(event); mSuspiciousTextDetected |= isSuspiciousText(event); if (mPasswordFieldDetected && mSuspiciousTextDetected) { ViewNode viewNode = event.getViewNode(); String eventText = ContentProtectionUtils.getEventTextLower(event); String viewNodeText = ContentProtectionUtils.getViewNodeTextLower(viewNode); String hintText = ContentProtectionUtils.getHintTextLower(viewNode); mGroupsAll.stream() .filter(group -> !group.mFound) .filter( group -> group.matches(eventText) || group.matches(viewNodeText) || group.matches(hintText)) .findFirst() .ifPresent( group -> { group.mFound = true; mAnyGroupFound = true; }); boolean loginDetected = mGroupsRequired.stream().allMatch(group -> group.mFound) && mGroupsOptional.stream().filter(group -> group.mFound).count() >= mOptions.optionalGroupsThreshold; if (loginDetected) { loginDetected(); } else { maybeResetLoginFlags(); Loading @@ -150,14 +165,13 @@ public class ContentProtectionEventProcessor { @UiThread private void resetLoginFlags() { mPasswordFieldDetected = false; mSuspiciousTextDetected = false; mResetLoginRemainingEventsToProcess = 0; mGroupsAll.forEach(group -> group.mFound = false); mAnyGroupFound = false; } @UiThread private void maybeResetLoginFlags() { if (mPasswordFieldDetected || mSuspiciousTextDetected) { if (mAnyGroupFound) { if (mResetLoginRemainingEventsToProcess <= 0) { mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; } else { Loading Loading @@ -194,61 +208,21 @@ public class ContentProtectionEventProcessor { } } private boolean isPasswordField(@NonNull ContentCaptureEvent event) { return isPasswordField(event.getViewNode()); } private static final class SearchGroup { private boolean isPasswordField(@Nullable ViewNode viewNode) { if (viewNode == null) { return false; } return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode); } @NonNull private final List<String> mSearchStrings; private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) { if (!isAndroidViewNode(viewNode)) { return false; } int inputType = viewNode.getInputType(); return PASSWORD_FIELD_INPUT_TYPES.stream() .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0); } private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) { if (viewNode.getClassName() != null) { return false; } return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode)); } public boolean mFound = false; private boolean isAndroidViewNode(@NonNull ViewNode viewNode) { String className = viewNode.getClassName(); return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX); SearchGroup(@NonNull List<String> searchStrings) { mSearchStrings = searchStrings; } private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) { return isSuspiciousText(ContentProtectionUtils.getEventText(event)) || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event)); } private boolean isSuspiciousText(@Nullable String text) { public boolean matches(@Nullable String text) { if (text == null) { return false; } if (isPasswordText(text)) { return true; } String lowerCaseText = text.toLowerCase(); return ADDITIONAL_SUSPICIOUS_TEXTS.stream() .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText)); } private boolean isPasswordText(@Nullable String text) { if (text == null) { return false; return mSearchStrings.stream().anyMatch(text::contains); } String lowerCaseText = text.toLowerCase(); return PASSWORD_TEXTS.stream() .anyMatch(passwordText -> lowerCaseText.contains(passwordText)); } } core/java/android/view/contentprotection/ContentProtectionUtils.java +17 −11 Original line number Diff line number Diff line Loading @@ -28,33 +28,39 @@ import android.view.contentcapture.ViewNode; */ public final class ContentProtectionUtils { /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */ /** Returns the lowercase text extracted from the {@link ContentCaptureEvent}, if set. */ @Nullable public static String getEventText(@NonNull ContentCaptureEvent event) { public static String getEventTextLower(@NonNull ContentCaptureEvent event) { CharSequence text = event.getText(); if (text == null) { return null; } return text.toString(); return text.toString().toLowerCase(); } /** Returns the text extracted from the event's {@link ViewNode}, if set. */ /** Returns the lowercase text extracted from the {@link ViewNode}, if set. */ @Nullable public static String getViewNodeText(@NonNull ContentCaptureEvent event) { ViewNode viewNode = event.getViewNode(); public static String getViewNodeTextLower(@Nullable ViewNode viewNode) { if (viewNode == null) { return null; } return getViewNodeText(viewNode); CharSequence text = viewNode.getText(); if (text == null) { return null; } return text.toString().toLowerCase(); } /** Returns the text extracted directly from the {@link ViewNode}, if set. */ /** Returns the lowercase hint text extracted from the {@link ViewNode}, if set. */ @Nullable public static String getViewNodeText(@NonNull ViewNode viewNode) { CharSequence text = viewNode.getText(); public static String getHintTextLower(@Nullable ViewNode viewNode) { if (viewNode == null) { return null; } String text = viewNode.getHint(); if (text == null) { return null; } return text.toString(); return text.toLowerCase(); } } core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java +21 −1 Original line number Diff line number Diff line Loading @@ -115,6 +115,26 @@ public class MainContentCaptureSessionTest { new ContentCaptureOptions.ContentProtectionOptions( /* enableReceiver= */ true, -BUFFER_SIZE, /* requiredGroups= */ List.of(List.of("a")), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); MainContentCaptureSession session = createSession(options); session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); assertThat(session.mContentProtectionEventProcessor).isNull(); verifyZeroInteractions(mMockContentProtectionEventProcessor); } @Test public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() { ContentCaptureOptions options = createOptions( /* enableContentCaptureReceiver= */ true, new ContentCaptureOptions.ContentProtectionOptions( /* enableReceiver= */ true, BUFFER_SIZE, /* requiredGroups= */ Collections.emptyList(), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); Loading Loading @@ -320,7 +340,7 @@ public class MainContentCaptureSessionTest { new ContentCaptureOptions.ContentProtectionOptions( enableContentProtectionReceiver, BUFFER_SIZE, /* requiredGroups= */ Collections.emptyList(), /* requiredGroups= */ List.of(List.of("a")), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); } Loading core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java +197 −299 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/contentcapture/MainContentCaptureSession.java +14 −5 Original line number Diff line number Diff line Loading @@ -317,16 +317,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } // Should not be possible for mComponentName to be null here but check anyway if (mManager.mOptions.contentProtectionOptions.enableReceiver && mManager.getContentProtectionEventBuffer() != null && mComponentName != null) { if (isContentProtectionEnabled()) { mContentProtectionEventProcessor = new ContentProtectionEventProcessor( mManager.getContentProtectionEventBuffer(), mHandler, mSystemServerInterface, mComponentName.getPackageName()); mComponentName.getPackageName(), mManager.mOptions.contentProtectionOptions); } else { mContentProtectionEventProcessor = null; } Loading Loading @@ -956,4 +954,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private boolean isContentCaptureReceiverEnabled() { return mManager.mOptions.enableReceiver; } @UiThread private boolean isContentProtectionEnabled() { // Should not be possible for mComponentName to be null here but check anyway // Should not be possible for groups to be empty if receiver is enabled but check anyway return mManager.mOptions.contentProtectionOptions.enableReceiver && mManager.getContentProtectionEventBuffer() != null && mComponentName != null && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty() || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty()); } }
core/java/android/view/contentprotection/ContentProtectionEventProcessor.java +64 −90 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package android.view.contentprotection; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; import android.content.ContentCaptureOptions; import android.content.pm.ParceledListSlice; import android.os.Handler; import android.text.InputType; import android.util.Log; import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.IContentCaptureManager; Loading @@ -33,10 +33,10 @@ import com.android.internal.util.RingBuffer; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Stream; /** * Main entry point for processing {@link ContentCaptureEvent} for the content protection flow. Loading @@ -47,33 +47,13 @@ public class ContentProtectionEventProcessor { private static final String TAG = "ContentProtectionEventProcessor"; private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES = Collections.unmodifiableList( Arrays.asList( InputType.TYPE_NUMBER_VARIATION_PASSWORD, InputType.TYPE_TEXT_VARIATION_PASSWORD, InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)); private static final List<String> PASSWORD_TEXTS = Collections.unmodifiableList( Arrays.asList("password", "pass word", "code", "pin", "credential")); private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS = Collections.unmodifiableList( Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in")); private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3); private static final String ANDROID_CLASS_NAME_PREFIX = "android."; private static final Set<Integer> EVENT_TYPES_TO_STORE = Collections.unmodifiableSet( new HashSet<>( Arrays.asList( Set.of( ContentCaptureEvent.TYPE_VIEW_APPEARED, ContentCaptureEvent.TYPE_VIEW_DISAPPEARED, ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED))); ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED); private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; Loading @@ -85,11 +65,7 @@ public class ContentProtectionEventProcessor { @NonNull private final String mPackageName; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public boolean mPasswordFieldDetected = false; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public boolean mSuspiciousTextDetected = false; @NonNull private final ContentCaptureOptions.ContentProtectionOptions mOptions; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @Nullable Loading @@ -97,15 +73,32 @@ public class ContentProtectionEventProcessor { private int mResetLoginRemainingEventsToProcess; private boolean mAnyGroupFound = false; // Ordered by priority private final List<SearchGroup> mGroupsRequired; // Ordered by priority private final List<SearchGroup> mGroupsOptional; // Ordered by priority private final List<SearchGroup> mGroupsAll; public ContentProtectionEventProcessor( @NonNull RingBuffer<ContentCaptureEvent> eventBuffer, @NonNull Handler handler, @NonNull IContentCaptureManager contentCaptureManager, @NonNull String packageName) { @NonNull String packageName, @NonNull ContentCaptureOptions.ContentProtectionOptions options) { mEventBuffer = eventBuffer; mHandler = handler; mContentCaptureManager = contentCaptureManager; mPackageName = packageName; mOptions = options; mGroupsRequired = options.requiredGroups.stream().map(SearchGroup::new).toList(); mGroupsOptional = options.optionalGroups.stream().map(SearchGroup::new).toList(); mGroupsAll = Stream.of(mGroupsRequired, mGroupsOptional).flatMap(Collection::stream).toList(); } /** Main entry point for {@link ContentCaptureEvent} processing. */ Loading @@ -130,9 +123,31 @@ public class ContentProtectionEventProcessor { @UiThread private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) { mPasswordFieldDetected |= isPasswordField(event); mSuspiciousTextDetected |= isSuspiciousText(event); if (mPasswordFieldDetected && mSuspiciousTextDetected) { ViewNode viewNode = event.getViewNode(); String eventText = ContentProtectionUtils.getEventTextLower(event); String viewNodeText = ContentProtectionUtils.getViewNodeTextLower(viewNode); String hintText = ContentProtectionUtils.getHintTextLower(viewNode); mGroupsAll.stream() .filter(group -> !group.mFound) .filter( group -> group.matches(eventText) || group.matches(viewNodeText) || group.matches(hintText)) .findFirst() .ifPresent( group -> { group.mFound = true; mAnyGroupFound = true; }); boolean loginDetected = mGroupsRequired.stream().allMatch(group -> group.mFound) && mGroupsOptional.stream().filter(group -> group.mFound).count() >= mOptions.optionalGroupsThreshold; if (loginDetected) { loginDetected(); } else { maybeResetLoginFlags(); Loading @@ -150,14 +165,13 @@ public class ContentProtectionEventProcessor { @UiThread private void resetLoginFlags() { mPasswordFieldDetected = false; mSuspiciousTextDetected = false; mResetLoginRemainingEventsToProcess = 0; mGroupsAll.forEach(group -> group.mFound = false); mAnyGroupFound = false; } @UiThread private void maybeResetLoginFlags() { if (mPasswordFieldDetected || mSuspiciousTextDetected) { if (mAnyGroupFound) { if (mResetLoginRemainingEventsToProcess <= 0) { mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; } else { Loading Loading @@ -194,61 +208,21 @@ public class ContentProtectionEventProcessor { } } private boolean isPasswordField(@NonNull ContentCaptureEvent event) { return isPasswordField(event.getViewNode()); } private static final class SearchGroup { private boolean isPasswordField(@Nullable ViewNode viewNode) { if (viewNode == null) { return false; } return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode); } @NonNull private final List<String> mSearchStrings; private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) { if (!isAndroidViewNode(viewNode)) { return false; } int inputType = viewNode.getInputType(); return PASSWORD_FIELD_INPUT_TYPES.stream() .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0); } private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) { if (viewNode.getClassName() != null) { return false; } return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode)); } public boolean mFound = false; private boolean isAndroidViewNode(@NonNull ViewNode viewNode) { String className = viewNode.getClassName(); return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX); SearchGroup(@NonNull List<String> searchStrings) { mSearchStrings = searchStrings; } private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) { return isSuspiciousText(ContentProtectionUtils.getEventText(event)) || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event)); } private boolean isSuspiciousText(@Nullable String text) { public boolean matches(@Nullable String text) { if (text == null) { return false; } if (isPasswordText(text)) { return true; } String lowerCaseText = text.toLowerCase(); return ADDITIONAL_SUSPICIOUS_TEXTS.stream() .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText)); } private boolean isPasswordText(@Nullable String text) { if (text == null) { return false; return mSearchStrings.stream().anyMatch(text::contains); } String lowerCaseText = text.toLowerCase(); return PASSWORD_TEXTS.stream() .anyMatch(passwordText -> lowerCaseText.contains(passwordText)); } }
core/java/android/view/contentprotection/ContentProtectionUtils.java +17 −11 Original line number Diff line number Diff line Loading @@ -28,33 +28,39 @@ import android.view.contentcapture.ViewNode; */ public final class ContentProtectionUtils { /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */ /** Returns the lowercase text extracted from the {@link ContentCaptureEvent}, if set. */ @Nullable public static String getEventText(@NonNull ContentCaptureEvent event) { public static String getEventTextLower(@NonNull ContentCaptureEvent event) { CharSequence text = event.getText(); if (text == null) { return null; } return text.toString(); return text.toString().toLowerCase(); } /** Returns the text extracted from the event's {@link ViewNode}, if set. */ /** Returns the lowercase text extracted from the {@link ViewNode}, if set. */ @Nullable public static String getViewNodeText(@NonNull ContentCaptureEvent event) { ViewNode viewNode = event.getViewNode(); public static String getViewNodeTextLower(@Nullable ViewNode viewNode) { if (viewNode == null) { return null; } return getViewNodeText(viewNode); CharSequence text = viewNode.getText(); if (text == null) { return null; } return text.toString().toLowerCase(); } /** Returns the text extracted directly from the {@link ViewNode}, if set. */ /** Returns the lowercase hint text extracted from the {@link ViewNode}, if set. */ @Nullable public static String getViewNodeText(@NonNull ViewNode viewNode) { CharSequence text = viewNode.getText(); public static String getHintTextLower(@Nullable ViewNode viewNode) { if (viewNode == null) { return null; } String text = viewNode.getHint(); if (text == null) { return null; } return text.toString(); return text.toLowerCase(); } }
core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java +21 −1 Original line number Diff line number Diff line Loading @@ -115,6 +115,26 @@ public class MainContentCaptureSessionTest { new ContentCaptureOptions.ContentProtectionOptions( /* enableReceiver= */ true, -BUFFER_SIZE, /* requiredGroups= */ List.of(List.of("a")), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); MainContentCaptureSession session = createSession(options); session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); assertThat(session.mContentProtectionEventProcessor).isNull(); verifyZeroInteractions(mMockContentProtectionEventProcessor); } @Test public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() { ContentCaptureOptions options = createOptions( /* enableContentCaptureReceiver= */ true, new ContentCaptureOptions.ContentProtectionOptions( /* enableReceiver= */ true, BUFFER_SIZE, /* requiredGroups= */ Collections.emptyList(), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); Loading Loading @@ -320,7 +340,7 @@ public class MainContentCaptureSessionTest { new ContentCaptureOptions.ContentProtectionOptions( enableContentProtectionReceiver, BUFFER_SIZE, /* requiredGroups= */ Collections.emptyList(), /* requiredGroups= */ List.of(List.of("a")), /* optionalGroups= */ Collections.emptyList(), /* optionalGroupsThreshold= */ 0)); } Loading
core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java +197 −299 File changed.Preview size limit exceeded, changes collapsed. Show changes