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

Commit aacac4a7 authored by MingWei's avatar MingWei
Browse files

Batch event before start processing them.

Test: CtsContentCaptureServiceTestCases, MainContentCaptureSessionTest.
BUG: 309411951
Change-Id: I5540b1e01281cbad0de352b8702a567b7054875b
parent e2d61b7b
Loading
Loading
Loading
Loading
+93 −34
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.Trace;
import android.service.contentcapture.ContentCaptureService;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
@@ -69,6 +70,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@@ -152,7 +154,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
    public ComponentName mComponentName;

    /**
     * List of events held to be sent as a batch.
     * Thread-safe queue of events held to be processed as a batch.
     *
     * Because it is not guaranteed that the events will be enqueued from a single thread, the
     * implementation must be thread-safe to prevent unexpected behaviour.
     */
    @NonNull
    private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;

    /**
     * List of events held to be sent to the {@link ContentCaptureService} as a batch.
     *
     * @hide
     */
@@ -238,6 +249,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;

        mSessionStateReceiver = new SessionStateReceiver(this);

        mEventProcessQueue = new ConcurrentLinkedQueue<>();
    }

    @Override
@@ -733,6 +746,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        }
        mDirectServiceInterface = null;
        mContentProtectionEventProcessor = null;
        if (runOnBackgroundThreadEnabled()) {
            mEventProcessQueue.clear();
        }
    }

    // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
@@ -823,27 +839,30 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
    // change should also get get rid of the "internalNotifyXXXX" methods above
    void notifyChildSessionStarted(int parentSessionId, int childSessionId,
            @NonNull ContentCaptureContext clientContext) {
        runOnContentCaptureThread(
                () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                .setParentSessionId(parentSessionId).setClientContext(clientContext),
                FORCE_FLUSH));
        final ContentCaptureEvent event =
                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                        .setParentSessionId(parentSessionId)
                        .setClientContext(clientContext);
        enqueueEvent(event, FORCE_FLUSH);
    }

    void notifyChildSessionFinished(int parentSessionId, int childSessionId) {
        runOnContentCaptureThread(
                () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
                .setParentSessionId(parentSessionId), FORCE_FLUSH));
        final ContentCaptureEvent event =
                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
                        .setParentSessionId(parentSessionId);
        enqueueEvent(event, FORCE_FLUSH);
    }

    void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
        runOnContentCaptureThread(() ->
                sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
                .setViewNode(node.mNode)));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
                .setViewNode(node.mNode);
        enqueueEvent(event);
    }

    void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
        runOnContentCaptureThread(() -> sendEvent(
                new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
                .setAutofillId(id);
        enqueueEvent(event);
    }

    void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
@@ -867,50 +886,90 @@ public final class MainContentCaptureSession extends ContentCaptureSession {

        final int startIndex = Selection.getSelectionStart(text);
        final int endIndex = Selection.getSelectionEnd(text);
        runOnContentCaptureThread(() -> sendEvent(
                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)

        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
                .setAutofillId(id).setText(eventText)
                .setComposingIndex(composingStart, composingEnd)
                        .setSelectionIndex(startIndex, endIndex)));
                .setSelectionIndex(startIndex, endIndex);
        enqueueEvent(event);
    }

    void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
        runOnContentCaptureThread(() ->
                sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
                .setInsets(viewInsets)));
        final ContentCaptureEvent event =
                new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
                        .setInsets(viewInsets);
        enqueueEvent(event);
    }

    void notifyViewTreeEvent(int sessionId, boolean started) {
        final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
        final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
        final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;

        runOnContentCaptureThread(() -> sendEvent(
                new ContentCaptureEvent(sessionId, type),
                disableFlush ? !started : FORCE_FLUSH));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
        enqueueEvent(event, forceFlush);
    }

    void notifySessionResumed(int sessionId) {
        runOnContentCaptureThread(() -> sendEvent(
                new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED), FORCE_FLUSH));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED);
        enqueueEvent(event, FORCE_FLUSH);
    }

    void notifySessionPaused(int sessionId) {
        runOnContentCaptureThread(() -> sendEvent(
                new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED), FORCE_FLUSH));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED);
        enqueueEvent(event, FORCE_FLUSH);
    }

    void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
        runOnContentCaptureThread(() ->
                sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
                .setClientContext(context), FORCE_FLUSH));
        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
                .setClientContext(context);
        enqueueEvent(event, FORCE_FLUSH);
    }

    /** public because is also used by ViewRootImpl */
    public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
        runOnContentCaptureThread(() -> sendEvent(
        final ContentCaptureEvent event =
                new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
                .setBounds(bounds)
        ));
                        .setBounds(bounds);
        enqueueEvent(event);
    }

    private List<ContentCaptureEvent> clearBufferEvents() {
        final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
        ContentCaptureEvent event;
        while ((event = mEventProcessQueue.poll()) != null) {
            bufferEvents.add(event);
        }
        return bufferEvents;
    }

    private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
        enqueueEvent(event, /* forceFlush */ false);
    }

    /**
     * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
     * clear the buffer events then starting sending out current event.
     */
    private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
        if (runOnBackgroundThreadEnabled()) {
            if (forceFlush) {
                // The buffer events are cleared in the same thread first to prevent new events
                // being added during the time of context switch. This would disrupt the sequence
                // of events.
                final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
                runOnContentCaptureThread(() -> {
                    for (int i = 0; i < batchEvents.size(); i++) {
                        sendEvent(batchEvents.get(i));
                    }
                    sendEvent(event, /* forceFlush= */ true);
                });
            } else {
                mEventProcessQueue.offer(event);
            }
        } else {
            mHandler.post(() -> sendEvent(event, forceFlush));
        }
    }

    /** public because is also used by ViewRootImpl */