diff options
| -rw-r--r-- | core/java/android/view/contentcapture/MainContentCaptureSession.java | 127 |
1 files changed, 93 insertions, 34 deletions
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 966161fd642a..19ba316257a3 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -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) - .setAutofillId(id).setText(eventText) - .setComposingIndex(composingStart, composingEnd) - .setSelectionIndex(startIndex, endIndex))); + + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED) + .setAutofillId(id).setText(eventText) + .setComposingIndex(composingStart, composingEnd) + .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 */ |