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

Commit e0dfa786 authored by Nino Jagar's avatar Nino Jagar Committed by Automerger Merge Worker
Browse files

Merge "Connect protection event processor with remote service" into...

Merge "Connect protection event processor with remote service" into udc-qpr-dev am: ab8d6382 am: c4ed2db0

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23516433



Change-Id: I996d8fc13fe0936d20ec0f0fe9bbc5b92c01d823
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 06d1123d c4ed2db0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ import java.io.PrintWriter;
 *
 * @hide
 */
public final class ContentCaptureServiceInfo {
public class ContentCaptureServiceInfo {

    private static final String TAG = ContentCaptureServiceInfo.class.getSimpleName();
    private static final String XML_TAG_SERVICE = "content-capture-service";
+6 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.view.contentcapture;

import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.DataRemovalRequest;
@@ -108,4 +109,9 @@ oneway interface IContentCaptureManager {
     */
    void registerContentCaptureOptionsCallback(String packageName,
                                               in IContentCaptureOptionsCallback callback);

    /**
     * Notifies the system server that a login was detected.
     */
    void onLoginDetected(in ParceledListSlice<ContentCaptureEvent> events);
}
+1 −1
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ public class ContentProtectionEventProcessor {

    private void handlerOnLoginDetected(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
        try {
            // TODO(b/275732576): Call mContentCaptureManager
            mContentCaptureManager.onLoginDetected(events);
        } catch (Exception ex) {
            Log.e(TAG, "Failed to flush events for: " + mPackageName, ex);
        }
+48 −9
Original line number Diff line number Diff line
@@ -26,10 +26,12 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
import android.os.Looper;
import android.text.InputType;
@@ -51,6 +53,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -82,6 +85,9 @@ public class ContentProtectionEventProcessorTest {

    private static final ContentCaptureEvent PROCESS_EVENT = createProcessEvent();

    private static final ContentCaptureEvent[] BUFFERED_EVENTS =
            new ContentCaptureEvent[] {PROCESS_EVENT};

    private static final Set<Integer> EVENT_TYPES_TO_STORE =
            ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED);

@@ -165,11 +171,12 @@ public class ContentProtectionEventProcessorTest {

        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
    public void processEvent_loginDetected() {
        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[0]);
    public void processEvent_loginDetected() throws Exception {
        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;

@@ -179,6 +186,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer).clear();
        verify(mMockEventBuffer).toArray();
        assertOnLoginDetected();
    }

    @Test
@@ -192,6 +200,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -205,6 +214,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -218,11 +228,12 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
    public void processEvent_multipleLoginsDetected_belowFlushThreshold() {
        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[0]);
    public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception {
        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);

        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
@@ -236,11 +247,12 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer).clear();
        verify(mMockEventBuffer).toArray();
        assertOnLoginDetected();
    }

    @Test
    public void processEvent_multipleLoginsDetected_aboveFlushThreshold() {
        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[0]);
    public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception {
        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);

        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
@@ -256,6 +268,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer, times(2)).clear();
        verify(mMockEventBuffer, times(2)).toArray();
        assertOnLoginDetected(PROCESS_EVENT, /* times= */ 2);
    }

    @Test
@@ -269,6 +282,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -282,6 +296,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -296,6 +311,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -309,21 +325,22 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
    public void isPasswordField_webView() {
        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[0]);

    public void isPasswordField_webView() throws Exception {
        ContentCaptureEvent event =
                createWebViewPasswordFieldEvent(
                        /* className= */ null, /* eventText= */ null, PASSWORD_TEXT);
        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[] {event});

        mContentProtectionEventProcessor.processEvent(event);

        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer).clear();
        verify(mMockEventBuffer).toArray();
        assertOnLoginDetected(event, /* times= */ 1);
    }

    @Test
@@ -337,6 +354,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -350,6 +368,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -362,6 +381,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -373,6 +393,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -384,6 +405,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -395,6 +417,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -406,6 +429,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    @Test
@@ -420,6 +444,7 @@ public class ContentProtectionEventProcessorTest {
        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
        verify(mMockEventBuffer, never()).clear();
        verify(mMockEventBuffer, never()).toArray();
        verifyZeroInteractions(mMockContentCaptureManager);
    }

    private static ContentCaptureEvent createEvent(int type) {
@@ -474,4 +499,18 @@ public class ContentProtectionEventProcessorTest {
        return createProcessEvent(
                /* className= */ null, /* inputType= */ 0, eventText, viewNodeText);
    }

    private void assertOnLoginDetected() throws Exception {
        assertOnLoginDetected(PROCESS_EVENT, /* times= */ 1);
    }

    private void assertOnLoginDetected(ContentCaptureEvent event, int times) throws Exception {
        ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
        verify(mMockContentCaptureManager, times(times)).onLoginDetected(captor.capture());

        assertThat(captor.getValue()).isNotNull();
        List<ContentCaptureEvent> actual = captor.getValue().getList();
        assertThat(actual).isNotNull();
        assertThat(actual).containsExactly(event);
    }
}
+106 −7
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.content.Context;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Binder;
@@ -69,6 +70,7 @@ import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.ContentCaptureServiceInfo;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.IDataShareReadAdapter;
import android.service.voice.VoiceInteractionManagerInternal;
@@ -79,6 +81,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureHelper;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.DataRemovalRequest;
@@ -96,6 +99,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.contentprotection.ContentProtectionBlocklistManager;
import com.android.server.contentprotection.ContentProtectionPackageManager;
import com.android.server.contentprotection.RemoteContentProtectionService;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;

@@ -208,6 +212,8 @@ public class ContentCaptureManagerService extends
    final GlobalContentCaptureOptions mGlobalContentCaptureOptions =
            new GlobalContentCaptureOptions();

    @Nullable private final ComponentName mContentProtectionServiceComponentName;

    @Nullable private final ContentProtectionBlocklistManager mContentProtectionBlocklistManager;

    public ContentCaptureManagerService(@NonNull Context context) {
@@ -249,12 +255,18 @@ public class ContentCaptureManagerService extends
        }

        if (getEnableContentProtectionReceiverLocked()) {
            mContentProtectionBlocklistManager = createContentProtectionBlocklistManager(context);
            mContentProtectionServiceComponentName = getContentProtectionServiceComponentName();
            if (mContentProtectionServiceComponentName != null) {
                mContentProtectionBlocklistManager = createContentProtectionBlocklistManager();
                mContentProtectionBlocklistManager.updateBlocklist(
                        mDevCfgContentProtectionAppsBlocklistSize);
            } else {
                mContentProtectionBlocklistManager = null;
            }
        } else {
            mContentProtectionServiceComponentName = null;
            mContentProtectionBlocklistManager = null;
        }
    }

    @Override // from AbstractMasterSystemService
@@ -785,9 +797,82 @@ public class ContentCaptureManagerService extends
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @NonNull
    protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager(
            @NonNull Context context) {
        return new ContentProtectionBlocklistManager(new ContentProtectionPackageManager(context));
    protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager() {
        return new ContentProtectionBlocklistManager(
                new ContentProtectionPackageManager(getContext()));
    }

    @Nullable
    private ComponentName getContentProtectionServiceComponentName() {
        String flatComponentName = getContentProtectionServiceFlatComponentName();
        ComponentName componentName = ComponentName.unflattenFromString(flatComponentName);
        if (componentName == null) {
            return null;
        }

        // Check permissions by trying to construct {@link ContentCaptureServiceInfo}
        try {
            createContentProtectionServiceInfo(componentName);
        } catch (Exception ex) {
            // Swallow, exception was already logged
            return null;
        }

        return componentName;
    }

    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @Nullable
    protected String getContentProtectionServiceFlatComponentName() {
        return getContext()
                .getString(com.android.internal.R.string.config_defaultContentProtectionService);
    }

    /**
     * Can also throw runtime exceptions such as {@link SecurityException}.
     *
     * @hide
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @NonNull
    protected ContentCaptureServiceInfo createContentProtectionServiceInfo(
            @NonNull ComponentName componentName) throws PackageManager.NameNotFoundException {
        return new ContentCaptureServiceInfo(
                getContext(), componentName, /* isTemp= */ false, UserHandle.getCallingUserId());
    }

    @Nullable
    private RemoteContentProtectionService createRemoteContentProtectionService() {
        if (mContentProtectionServiceComponentName == null) {
            // This case should not be possible but make sure
            return null;
        }
        synchronized (mLock) {
            if (!mDevCfgEnableContentProtectionReceiver) {
                return null;
            }
        }
        return createRemoteContentProtectionService(mContentProtectionServiceComponentName);
    }

    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @NonNull
    protected RemoteContentProtectionService createRemoteContentProtectionService(
            @NonNull ComponentName componentName) {
        return new RemoteContentProtectionService(
                getContext(),
                componentName,
                UserHandle.getCallingUserId(),
                isBindInstantServiceAllowed());
    }

    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @NonNull
    protected ContentCaptureManagerServiceStub getContentCaptureManagerServiceStub() {
        return mContentCaptureManagerServiceStub;
    }

    final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@@ -1023,6 +1108,19 @@ public class ContentCaptureManagerService extends
        public void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
            ContentCaptureManagerService.this.setDefaultServiceEnabled(userId, enabled);
        }

        @Override
        public void onLoginDetected(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
            RemoteContentProtectionService service = createRemoteContentProtectionService();
            if (service == null) {
                return;
            }
            try {
                service.onLoginDetected(events);
            } catch (Exception ex) {
                Slog.e(TAG, "Failed to call remote service", ex);
            }
        }
    }

    private final class LocalService extends ContentCaptureManagerInternal {
@@ -1205,7 +1303,8 @@ public class ContentCaptureManagerService extends
        }

        private boolean isContentProtectionReceiverEnabled(@NonNull String packageName) {
            if (mContentProtectionBlocklistManager == null) {
            if (mContentProtectionServiceComponentName == null
                    || mContentProtectionBlocklistManager == null) {
                return false;
            }
            synchronized (mLock) {
Loading