From 810ddb5e27bf364138072be48c583eceaa89b61e Mon Sep 17 00:00:00 2001 From: Nino Jagar Date: Mon, 5 Jun 2023 23:29:36 +0000 Subject: [PATCH] Add a reset mechanism into content protection processor Bug: 275732576 Test: Added tests and manual end to end Change-Id: I72238dae65b634c54cc62a0f2df4dff5816ae1c3 --- .../ContentProtectionEventProcessor.java | 26 ++++++++++ .../ContentProtectionEventProcessorTest.java | 52 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java index e49df21692c58..b44abf3eb04d2 100644 --- a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java +++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java @@ -75,6 +75,8 @@ public class ContentProtectionEventProcessor { ContentCaptureEvent.TYPE_VIEW_DISAPPEARED, ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED))); + private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; + @NonNull private final RingBuffer mEventBuffer; @NonNull private final Handler mHandler; @@ -93,6 +95,8 @@ public class ContentProtectionEventProcessor { @Nullable public Instant mLastFlushTime; + private int mResetLoginRemainingEventsToProcess; + public ContentProtectionEventProcessor( @NonNull RingBuffer eventBuffer, @NonNull Handler handler, @@ -130,6 +134,8 @@ public class ContentProtectionEventProcessor { mSuspiciousTextDetected |= isSuspiciousText(event); if (mPasswordFieldDetected && mSuspiciousTextDetected) { loginDetected(); + } else { + maybeResetLoginFlags(); } } @@ -139,8 +145,28 @@ public class ContentProtectionEventProcessor { || Instant.now().isAfter(mLastFlushTime.plus(MIN_DURATION_BETWEEN_FLUSHING))) { flush(); } + resetLoginFlags(); + } + + @UiThread + private void resetLoginFlags() { mPasswordFieldDetected = false; mSuspiciousTextDetected = false; + mResetLoginRemainingEventsToProcess = 0; + } + + @UiThread + private void maybeResetLoginFlags() { + if (mPasswordFieldDetected || mSuspiciousTextDetected) { + if (mResetLoginRemainingEventsToProcess <= 0) { + mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; + } else { + mResetLoginRemainingEventsToProcess--; + if (mResetLoginRemainingEventsToProcess <= 0) { + resetLoginFlags(); + } + } + } } @UiThread diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java index 4adadf11903ee..39a2e0e048e83 100644 --- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java +++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java @@ -91,6 +91,8 @@ public class ContentProtectionEventProcessorTest { private static final Set EVENT_TYPES_TO_STORE = ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED); + private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private RingBuffer mMockEventBuffer; @@ -231,6 +233,56 @@ public class ContentProtectionEventProcessorTest { verifyZeroInteractions(mMockContentCaptureManager); } + @Test + public void processEvent_loginDetected_belowResetLimit() throws Exception { + when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS); + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; i++) { + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + } + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer).clear(); + verify(mMockEventBuffer).toArray(); + assertOnLoginDetected(); + } + + @Test + public void processEvent_loginDetected_aboveResetLimit() throws Exception { + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS + 1; i++) { + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + } + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + } + @Test public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception { when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);