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

Commit 953d9873 authored by Adrian Roos's avatar Adrian Roos
Browse files

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
parent 687a05d3
Loading
Loading
Loading
Loading
+35 −37
Original line number Diff line number Diff line
@@ -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<C> 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 #requestTextBoundsInfo(RectF, Executor, Consumer)} for providing
     * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing
     * callback.
     */
    private static final class TextBoundsInfoResultReceiver extends ResultReceiver {
        @Nullable
        private Consumer<TextBoundsInfoResult> mConsumer;
        @Nullable
        private Executor mExecutor;

        TextBoundsInfoResultReceiver(@NonNull Executor executor,
                @NonNull Consumer<TextBoundsInfoResult> consumer) {
            super(null);
            mExecutor = executor;
            mConsumer = consumer;
    private static final class IntResultReceiver extends OnceResultReceiver<IntConsumer> {
        IntResultReceiver(@NonNull Executor executor, @NonNull IntConsumer 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();
        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
            OnceResultReceiver<Consumer<TextBoundsInfoResult>> {
        TextBoundsInfoResultReceiver(@NonNull Executor executor,
                @NonNull Consumer<TextBoundsInfoResult> consumer) {
            super(executor, consumer);
        }

        private void clear() {
            mExecutor = null;
            mConsumer = null;
        @Override
        protected void dispatch(@NonNull Executor executor,
                @NonNull Consumer<TextBoundsInfoResult> consumer, int code, Bundle data) {
            final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult(
                    code, TextBoundsInfo.createFromBundle(data));
            executor.execute(() -> consumer.accept(textBoundsInfoResult));
        }
    }