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
This commit is contained in:
@@ -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 #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing
|
||||
* callback.
|
||||
*/
|
||||
private static final class IntResultReceiver extends OnceResultReceiver<IntConsumer> {
|
||||
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<TextBoundsInfoResult> mConsumer;
|
||||
@Nullable
|
||||
private Executor mExecutor;
|
||||
|
||||
private static final class TextBoundsInfoResultReceiver extends
|
||||
OnceResultReceiver<Consumer<TextBoundsInfoResult>> {
|
||||
TextBoundsInfoResultReceiver(@NonNull Executor executor,
|
||||
@NonNull Consumer<TextBoundsInfoResult> 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<TextBoundsInfoResult> consumer, int code, Bundle data) {
|
||||
final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult(
|
||||
code, TextBoundsInfo.createFromBundle(data));
|
||||
executor.execute(() -> consumer.accept(textBoundsInfoResult));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user