From 953d9873f4e43f93e324d679b35ac25072cc1747 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 20 Jan 2023 10:23:16 +0100 Subject: [PATCH] IMF: Factor out OnceResultReceiver to fix TextBoundsInfoResultReceiver Fixes an issue with TextBoundsInfoResultReceiver, where any Executor that does not run the Runnable directly crashes, because the mConsumer is cleared before it is run. Also avoids calling into the executor with internal locks held. Bug: 265457058 Test: atest InputConnectionEndToEndTest Change-Id: I4b229494fb2451135ae1075272f04d865a9b509e --- .../IRemoteInputConnectionInvoker.java | 72 +++++++++---------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java index a75994c3c7ae9..31f095c8811cb 100644 --- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java +++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java @@ -66,18 +66,13 @@ final class IRemoteInputConnectionInvoker { mSessionId = sessionId; } - /** - * Subclass of {@link ResultReceiver} used by - * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing - * callback. - */ - private static final class IntResultReceiver extends ResultReceiver { + private abstract static class OnceResultReceiver extends ResultReceiver { @Nullable - private IntConsumer mConsumer; + private C mConsumer; @Nullable private Executor mExecutor; - IntResultReceiver(@NonNull Executor executor, @NonNull IntConsumer consumer) { + protected OnceResultReceiver(@NonNull Executor executor, @NonNull C consumer) { super(null); Objects.requireNonNull(executor); Objects.requireNonNull(consumer); @@ -86,9 +81,9 @@ final class IRemoteInputConnectionInvoker { } @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { + protected final void onReceiveResult(int resultCode, Bundle resultData) { final Executor executor; - final IntConsumer consumer; + final C consumer; synchronized (this) { executor = mExecutor; consumer = mConsumer; @@ -96,46 +91,49 @@ final class IRemoteInputConnectionInvoker { mConsumer = null; } if (executor != null && consumer != null) { - executor.execute(() -> consumer.accept(resultCode)); + dispatch(executor, consumer, resultCode, resultData); } } - }; + + protected abstract void dispatch(@NonNull Executor executor, @NonNull C consumer, int code, + Bundle data); + } + + /** + * Subclass of {@link ResultReceiver} used by + * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing + * callback. + */ + private static final class IntResultReceiver extends OnceResultReceiver { + IntResultReceiver(@NonNull Executor executor, @NonNull IntConsumer consumer) { + super(executor, consumer); + } + + @Override + protected void dispatch(@NonNull Executor executor, @NonNull IntConsumer consumer, int code, + Bundle data) { + executor.execute(() -> consumer.accept(code)); + } + } /** * Subclass of {@link ResultReceiver} used by * {@link #requestTextBoundsInfo(RectF, Executor, Consumer)} for providing * callback. */ - private static final class TextBoundsInfoResultReceiver extends ResultReceiver { - @Nullable - private Consumer mConsumer; - @Nullable - private Executor mExecutor; - + private static final class TextBoundsInfoResultReceiver extends + OnceResultReceiver> { TextBoundsInfoResultReceiver(@NonNull Executor executor, @NonNull Consumer consumer) { - super(null); - mExecutor = executor; - mConsumer = consumer; + super(executor, consumer); } @Override - protected void onReceiveResult(@TextBoundsInfoResult.ResultCode int resultCode, - @Nullable Bundle resultData) { - synchronized (this) { - if (mExecutor != null && mConsumer != null) { - final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult( - resultCode, TextBoundsInfo.createFromBundle(resultData)); - mExecutor.execute(() -> mConsumer.accept(textBoundsInfoResult)); - // provide callback only once. - clear(); - } - } - } - - private void clear() { - mExecutor = null; - mConsumer = null; + protected void dispatch(@NonNull Executor executor, + @NonNull Consumer consumer, int code, Bundle data) { + final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult( + code, TextBoundsInfo.createFromBundle(data)); + executor.execute(() -> consumer.accept(textBoundsInfoResult)); } }