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

Commit ac040fdd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Optimizes the Content Capture workflow by calling the service directly."

parents b0b45293 b9687849
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -357,6 +357,7 @@ java_defaults {
        "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
        "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
        "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
        "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
        "core/java/android/view/contentcapture/IContentCaptureManager.aidl",
        "core/java/android/view/IApplicationToken.aidl",
        "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
+1 −1
Original line number Diff line number Diff line
@@ -5017,7 +5017,7 @@ package android.service.contentcapture {
    method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
    method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
    method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
    method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
    method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
    method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
    method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
    method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+6 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package android.service.contentcapture;

import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureEvent;
@@ -31,10 +32,10 @@ import java.util.List;
@SystemApi
public final class ContentCaptureEventsRequest implements Parcelable {

    private final List<ContentCaptureEvent> mEvents;
    private final ParceledListSlice<ContentCaptureEvent> mEvents;

    /** @hide */
    public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
    public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
        mEvents = events;
    }

@@ -43,7 +44,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
     */
    @NonNull
    public List<ContentCaptureEvent> getEvents() {
        return mEvents;
        return mEvents.getList();
    }

    @Override
@@ -53,7 +54,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeTypedList(mEvents, flags);
        parcel.writeParcelable(mEvents, flags);
    }

    public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
@@ -61,8 +62,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {

        @Override
        public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
            return new ContentCaptureEventsRequest(parcel
                    .createTypedArrayList(ContentCaptureEvent.CREATOR));
            return new ContentCaptureEventsRequest(parcel.readParcelable(null));
        }

        @Override
+123 −31
Original line number Diff line number Diff line
@@ -24,15 +24,27 @@ import android.annotation.SystemApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.IContentCaptureDirectManager;

import com.android.internal.os.IResultReceiver;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;

@@ -63,39 +75,54 @@ public abstract class ContentCaptureService extends Service {

    private Handler mHandler;

    private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
    /**
     * Binder that receives calls from the system server.
     */
    private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {

        @Override
        public void onSessionLifecycle(ContentCaptureContext context, String sessionId)
                throws RemoteException {
            if (context != null) {
                mHandler.sendMessage(
                        obtainMessage(ContentCaptureService::handleOnCreateSession,
                                ContentCaptureService.this, context, sessionId));
            } else {
                mHandler.sendMessage(
                        obtainMessage(ContentCaptureService::handleOnDestroySession,
                                ContentCaptureService.this, sessionId));
            }
        public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
                IResultReceiver clientReceiver) {
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
                    ContentCaptureService.this, context, sessionId, uid, clientReceiver));
        }

        @Override
        public void onContentCaptureEventsRequest(String sessionId,
                ContentCaptureEventsRequest request) {
        public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
            mHandler.sendMessage(
                    obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
                            ContentCaptureService.this, sessionId, request));
                    obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
                            ContentCaptureService.this, sessionId, snapshotData));
        }

        @Override
        public void onSessionFinished(String sessionId) {
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
                    ContentCaptureService.this, sessionId));
        }
    };

    /**
     * Binder that receives calls from the app.
     */
    private final IContentCaptureDirectManager mClientInterface =
            new IContentCaptureDirectManager.Stub() {

        @Override
        public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
            mHandler.sendMessage(
                    obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
                            ContentCaptureService.this, sessionId, snapshotData));
        public void sendEvents(String sessionId,
                @SuppressWarnings("rawtypes") ParceledListSlice events) {
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
                            ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
        }
    };

    /**
     * List of sessions per UID.
     *
     * <p>This map is populated when an session is started, which is called by the system server
     * and can be trusted. Then subsequent calls made by the app are verified against this map.
     */
    private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();

    @CallSuper
    @Override
    public void onCreate() {
@@ -107,7 +134,7 @@ public abstract class ContentCaptureService extends Service {
    @Override
    public final IBinder onBind(Intent intent) {
        if (SERVICE_INTERFACE.equals(intent.getAction())) {
            return mInterface.asBinder();
            return mServerInterface.asBinder();
        }
        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
        return null;
@@ -196,8 +223,10 @@ public abstract class ContentCaptureService extends Service {
     * @param sessionId the session's Id
     * @param request the events
     */
    public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
            @NonNull ContentCaptureEventsRequest request);
    public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
            @NonNull ContentCaptureEventsRequest request) {
        if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
    }

    /**
     * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
@@ -212,10 +241,22 @@ public abstract class ContentCaptureService extends Service {
     * Destroys the content capture session.
     *
     * @param sessionId the id of the session to destroy
     */
     * */
    public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
        if (VERBOSE) {
            Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
        if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
    }

    @Override
    @CallSuper
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        final int size = mSessionsByUid.size();
        pw.print("Number sessions: "); pw.println(size);
        if (size > 0) {
            final String prefix = "  ";
            for (int i = 0; i < size; i++) {
                pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
                pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
            }
        }
    }

@@ -223,13 +264,19 @@ public abstract class ContentCaptureService extends Service {
    // so we don't need to create a temporary InteractionSessionId for each event.

    private void handleOnCreateSession(@NonNull ContentCaptureContext context,
            @NonNull String sessionId) {
            @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
        mSessionsByUid.put(sessionId, uid);
        onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
                mClientInterface.asBinder());
    }

    private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
            @NonNull ContentCaptureEventsRequest request) {
        onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request);
    private void handleSendEvents(@NonNull String sessionId, int uid,
            @NonNull ParceledListSlice<ContentCaptureEvent> events) {
        if (handleIsRightCallerFor(sessionId, uid)) {
            onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
                    new ContentCaptureEventsRequest(events));
        }
    }

    private void handleOnActivitySnapshot(@NonNull String sessionId,
@@ -237,7 +284,52 @@ public abstract class ContentCaptureService extends Service {
        onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
    }

    private void handleOnDestroySession(@NonNull String sessionId) {
    private void handleFinishSession(@NonNull String sessionId) {
        mSessionsByUid.remove(sessionId);
        onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
    }

    /**
     * Checks if the given {@code uid} owns the session.
     */
    private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
        final Integer rightUid = mSessionsByUid.get(sessionId);
        if (rightUid == null) {
            if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
            // Just ignore, as the session could have finished
            return false;
        }
        if (rightUid != uid) {
            Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
                    + rightUid);
            //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
            return false;
        }
        return true;

    }

    /**
     * Sends the state of the {@link ContentCaptureManager} in the cleint app.
     *
     * @param clientReceiver receiver in the client app.
     * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
     * service.
     * @hide
     */
    public static void setClientState(@NonNull IResultReceiver clientReceiver,
            int sessionStatus, @Nullable IBinder binder) {
        try {
            final Bundle extras;
            if (binder != null) {
                extras = new Bundle();
                extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
            } else {
                extras = null;
            }
            clientReceiver.send(sessionStatus, extras);
        } catch (RemoteException e) {
            Slog.w(TAG, "Error async reporting result to client: " + e);
        }
    }
}
+5 −7
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package android.service.contentcapture;

import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.SnapshotData;
import android.view.contentcapture.ContentCaptureContext;

import com.android.internal.os.IResultReceiver;

import java.util.List;

/**
@@ -28,11 +29,8 @@ import java.util.List;
 * @hide
 */
oneway interface IContentCaptureService {

    // Called when session is created (context not null) or destroyed (context null)
    void onSessionLifecycle(in ContentCaptureContext context, String sessionId);

    void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);

    void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
                          in IResultReceiver clientReceiver);
    void onSessionFinished(String sessionId);
    void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
}
Loading