diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index b52b437b45578..a298c856a0fba 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -18,6 +18,7 @@ package android.inputmethodservice; import android.annotation.BinderThread; import android.annotation.MainThread; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -37,6 +38,7 @@ import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; +import com.android.internal.inputmethod.CancellationGroup; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInlineSuggestionsRequestCallback; @@ -52,7 +54,6 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; /** * Implements the internal IInputMethod interface to convert incoming calls @@ -90,12 +91,13 @@ class IInputMethodWrapper extends IInputMethod.Stub * *
This field must be set and cleared only from the binder thread(s), where the system * guarantees that {@link #bindInput(InputBinding)}, - * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and + * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean, boolean)}, and * {@link #unbindInput()} are called with the same order as the original calls * in {@link com.android.server.inputmethod.InputMethodManagerService}. * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.
*/ - AtomicBoolean mIsUnbindIssued = null; + @Nullable + CancellationGroup mCancellationGroup = null; // NOTE: we should have a cache of these. static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { @@ -187,11 +189,11 @@ class IInputMethodWrapper extends IInputMethod.Stub final IBinder startInputToken = (IBinder) args.arg1; final IInputContext inputContext = (IInputContext) args.arg2; final EditorInfo info = (EditorInfo) args.arg3; - final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4; + final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4; SomeArgs moreArgs = (SomeArgs) args.arg5; final InputConnection ic = inputContext != null ? new InputConnectionWrapper( - mTarget, inputContext, moreArgs.argi3, isUnbindIssued) + mTarget, inputContext, moreArgs.argi3, cancellationGroup) : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken( @@ -295,15 +297,15 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void bindInput(InputBinding binding) { - if (mIsUnbindIssued != null) { + if (mCancellationGroup != null) { Log.e(TAG, "bindInput must be paired with unbindInput."); } - mIsUnbindIssued = new AtomicBoolean(); + mCancellationGroup = new CancellationGroup(); // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; InputConnection ic = new InputConnectionWrapper(mTarget, IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags, - mIsUnbindIssued); + mCancellationGroup); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } @@ -311,10 +313,10 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void unbindInput() { - if (mIsUnbindIssued != null) { + if (mCancellationGroup != null) { // Signal the flag then forget it. - mIsUnbindIssued.set(true); - mIsUnbindIssued = null; + mCancellationGroup.cancelAll(); + mCancellationGroup = null; } else { Log.e(TAG, "unbindInput must be paired with bindInput."); } @@ -326,16 +328,16 @@ class IInputMethodWrapper extends IInputMethod.Stub public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, EditorInfo attribute, boolean restarting, boolean shouldPreRenderIme) { - if (mIsUnbindIssued == null) { + if (mCancellationGroup == null) { Log.e(TAG, "startInput must be called after bindInput."); - mIsUnbindIssued = new AtomicBoolean(); + mCancellationGroup = new CancellationGroup(); } SomeArgs args = SomeArgs.obtain(); args.argi1 = restarting ? 1 : 0; args.argi2 = shouldPreRenderIme ? 1 : 0; args.argi3 = missingMethods; - mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO( - DO_START_INPUT, startInputToken, inputContext, attribute, mIsUnbindIssued, args)); + mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(DO_START_INPUT, startInputToken, + inputContext, attribute, mCancellationGroup, args)); } @BinderThread diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index ef138a0c2217e..dbb669be14027 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -39,6 +39,7 @@ import android.view.inputmethod.ExtractedText; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IMultiClientInputMethodSession; +import com.android.internal.inputmethod.CancellationGroup; import com.android.internal.os.SomeArgs; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.IInputContext; @@ -46,7 +47,6 @@ import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputConnectionWrapper; import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicBoolean; /** * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread. @@ -80,19 +80,19 @@ final class MultiClientInputMethodClientCallbackAdaptor { @Nullable InputEventReceiver mInputEventReceiver; - private final AtomicBoolean mFinished = new AtomicBoolean(false); + private final CancellationGroup mCancellationGroup = new CancellationGroup(); IInputMethodSession.Stub createIInputMethodSession() { synchronized (mSessionLock) { return new InputMethodSessionImpl( - mSessionLock, mCallbackImpl, mHandler, mFinished); + mSessionLock, mCallbackImpl, mHandler, mCancellationGroup); } } IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() { synchronized (mSessionLock) { return new MultiClientInputMethodSessionImpl( - mSessionLock, mCallbackImpl, mHandler, mFinished); + mSessionLock, mCallbackImpl, mHandler, mCancellationGroup); } } @@ -105,7 +105,7 @@ final class MultiClientInputMethodClientCallbackAdaptor { mHandler = new Handler(looper, null, true); mReadChannel = readChannel; mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(), - mFinished, mDispatcherState, mCallbackImpl.mOriginalCallback); + mCancellationGroup, mDispatcherState, mCallbackImpl.mOriginalCallback); } } @@ -139,16 +139,17 @@ final class MultiClientInputMethodClientCallbackAdaptor { } private static final class ImeInputEventReceiver extends InputEventReceiver { - private final AtomicBoolean mFinished; + private final CancellationGroup mCancellationGroupOnFinishSession; private final KeyEvent.DispatcherState mDispatcherState; private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback; private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor; - ImeInputEventReceiver(InputChannel readChannel, Looper looper, AtomicBoolean finished, + ImeInputEventReceiver(InputChannel readChannel, Looper looper, + CancellationGroup cancellationGroupOnFinishSession, KeyEvent.DispatcherState dispatcherState, MultiClientInputMethodServiceDelegate.ClientCallback callback) { super(readChannel, looper); - mFinished = finished; + mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession; mDispatcherState = dispatcherState; mClientCallback = callback; mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback); @@ -156,7 +157,7 @@ final class MultiClientInputMethodClientCallbackAdaptor { @Override public void onInputEvent(InputEvent event) { - if (mFinished.get()) { + if (mCancellationGroupOnFinishSession.isCanceled()) { // The session has been finished. finishInputEvent(event, false); return; @@ -187,14 +188,14 @@ final class MultiClientInputMethodClientCallbackAdaptor { private CallbackImpl mCallbackImpl; @GuardedBy("mSessionLock") private Handler mHandler; - private final AtomicBoolean mSessionFinished; + private final CancellationGroup mCancellationGroupOnFinishSession; InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler, - AtomicBoolean sessionFinished) { + CancellationGroup cancellationGroupOnFinishSession) { mSessionLock = lock; mCallbackImpl = callback; mHandler = handler; - mSessionFinished = sessionFinished; + mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession; } @Override @@ -272,7 +273,7 @@ final class MultiClientInputMethodClientCallbackAdaptor { if (mCallbackImpl == null || mHandler == null) { return; } - mSessionFinished.set(true); + mCancellationGroupOnFinishSession.cancelAll(); mHandler.sendMessage(PooledLambda.obtainMessage( CallbackImpl::finishSession, mCallbackImpl)); mCallbackImpl = null; @@ -311,14 +312,14 @@ final class MultiClientInputMethodClientCallbackAdaptor { private CallbackImpl mCallbackImpl; @GuardedBy("mSessionLock") private Handler mHandler; - private final AtomicBoolean mSessionFinished; + private final CancellationGroup mCancellationGroupOnFinishSession; MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback, - Handler handler, AtomicBoolean sessionFinished) { + Handler handler, CancellationGroup cancellationGroupOnFinishSession) { mSessionLock = lock; mCallbackImpl = callback; mHandler = handler; - mSessionFinished = sessionFinished; + mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession; } @Override @@ -335,7 +336,7 @@ final class MultiClientInputMethodClientCallbackAdaptor { new WeakReference<>(null); args.arg1 = (inputContext == null) ? null : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods, - mSessionFinished); + mCancellationGroupOnFinishSession); args.arg2 = editorInfo; args.argi1 = controlFlags; args.argi2 = softInputMode; diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java new file mode 100644 index 0000000000000..09c9d128553bc --- /dev/null +++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A utility class, which works as both a factory class of completable objects and a cancellation + * signal to cancel all the completable objects created by this object. + */ +public final class CancellationGroup { + private final Object mLock = new Object(); + + /** + * List of {@link CountDownLatch}, which can be used to propagate {@link #cancelAll()} to + * completable objects. + * + *This will be lazily instantiated to avoid unnecessary object allocations.
+ */ + @Nullable + @GuardedBy("mLock") + private ArrayList+ *
The caller can distinguish the case 1 and case 2 by calling {@link #hasValue()}. + * Note that the return value of {@link #hasValue()} can change from {@code false} to + * {@code true} at any time, even after this methods finishes with returning + * {@code true}.
+ * + * @param timeout length of the timeout. + * @param timeUnit unit of {@code timeout}. + * @return {@code false} if and only if the given timeout period has passed. Otherwise + * {@code true}. + */ + @AnyThread + public boolean await(int timeout, @NonNull TimeUnit timeUnit) { + if (!mParentGroup.registerLatch(mLatch)) { + // Already canceled when this method gets called. + return false; + } + try { + return mLatch.await(timeout, timeUnit); + } catch (InterruptedException e) { + return true; + } finally { + mParentGroup.unregisterLatch(mLatch); + } + } + } + + /** + * Completable object of integer primitive. + */ + public static final class Int extends ValueBase { + @GuardedBy("mValueLock") + private int mValue = 0; + + private Int(@NonNull CancellationGroup factory) { + super(factory); + } + + /** + * Notify when a value is set to this completable object. + * + * @param value value to be set. + */ + @AnyThread + void onComplete(int value) { + synchronized (mValueLock) { + if (mHasValue) { + throw new UnsupportedOperationException( + "onComplete() cannot be called multiple times"); + } + mValue = value; + mHasValue = true; + } + onComplete(); + } + + /** + * @return value associated with this object. + * @throws UnsupportedOperationException when called while {@link #hasValue()} returns + * {@code false}. + */ + @AnyThread + public int getValue() { + synchronized (mValueLock) { + if (!mHasValue) { + throw new UnsupportedOperationException( + "getValue() is allowed only if hasValue() returns true"); + } + return mValue; + } + } + } + + /** + * Base class of completable object types. + * + * @paramSecondary calls will be silently ignored.
+ */ + @AnyThread + public void cancelAll() { + synchronized (mLock) { + if (!mCanceled) { + mCanceled = true; + if (mLatchList != null) { + mLatchList.forEach(CountDownLatch::countDown); + mLatchList.clear(); + mLatchList = null; + } + } + } + } + + /** + * @return {@code true} if {@link #cancelAll()} is already called. {@code false} otherwise. + */ + @AnyThread + public boolean isCanceled() { + synchronized (mLock) { + return mCanceled; + } + } +} diff --git a/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl b/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl new file mode 100644 index 0000000000000..da56fd045e57d --- /dev/null +++ b/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +oneway interface ICharSequenceResultCallback { + void onResult(in CharSequence result); +} diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl similarity index 50% rename from core/java/com/android/internal/view/IInputContextCallback.aidl rename to core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl index 0f40a83d7ee4b..b603f6adc2d27 100644 --- a/core/java/com/android/internal/view/IInputContextCallback.aidl +++ b/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,10 @@ * limitations under the License. */ -package com.android.internal.view; +package com.android.internal.inputmethod; import android.view.inputmethod.ExtractedText; -/** - * {@hide} - */ -oneway interface IInputContextCallback { - void setTextBeforeCursor(CharSequence textBeforeCursor, int seq); - void setTextAfterCursor(CharSequence textAfterCursor, int seq); - void setCursorCapsMode(int capsMode, int seq); - void setExtractedText(in ExtractedText extractedText, int seq); - void setSelectedText(CharSequence selectedText, int seq); - void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq); - void setCommitContentResult(boolean result, int seq); +oneway interface IExtractedTextResultCallback { + void onResult(in ExtractedText result); } diff --git a/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl b/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl new file mode 100644 index 0000000000000..bc5ed0d38633a --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +oneway interface IIntResultCallback { + void onResult(int result); +} diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java new file mode 100644 index 0000000000000..44a8a83b519fd --- /dev/null +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.annotation.AnyThread; +import android.annotation.BinderThread; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Defines a set of factory methods to create {@link android.os.IBinder}-based callbacks that are + * associated with completable objects defined in {@link CancellationGroup.Completable}. + */ +public final class ResultCallbacks { + + /** + * Not intended to be instantiated. + */ + private ResultCallbacks() { + } + + @AnyThread + @Nullable + private staticThis is expected to be signaled immediately when the IME process receives + * {@link IInputMethod#unbindInput()}.
*/ @NonNull - private final AtomicBoolean mIsUnbindIssued; - - static class InputContextCallback extends IInputContextCallback.Stub { - private static final String TAG = "InputConnectionWrapper.ICC"; - public int mSeq; - public boolean mHaveValue; - public CharSequence mTextBeforeCursor; - public CharSequence mTextAfterCursor; - public CharSequence mSelectedText; - public ExtractedText mExtractedText; - public int mCursorCapsMode; - public boolean mRequestUpdateCursorAnchorInfoResult; - public boolean mCommitContentResult; - - // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain - // exclusive access to this object. - private static InputContextCallback sInstance = new InputContextCallback(); - private static int sSequenceNumber = 1; - - /** - * Returns an InputContextCallback object that is guaranteed not to be in use by - * any other thread. The returned object's 'have value' flag is cleared and its expected - * sequence number is set to a new integer. We use a sequence number so that replies that - * occur after a timeout has expired are not interpreted as replies to a later request. - */ - @UnsupportedAppUsage - @AnyThread - private static InputContextCallback getInstance() { - synchronized (InputContextCallback.class) { - // Return sInstance if it's non-null, otherwise construct a new callback - InputContextCallback callback; - if (sInstance != null) { - callback = sInstance; - sInstance = null; - - // Reset the callback - callback.mHaveValue = false; - } else { - callback = new InputContextCallback(); - } - - // Set the sequence number - callback.mSeq = sSequenceNumber++; - return callback; - } - } - - /** - * Makes the given InputContextCallback available for use in the future. - */ - @UnsupportedAppUsage - @AnyThread - private void dispose() { - synchronized (InputContextCallback.class) { - // If sInstance is non-null, just let this object be garbage-collected - if (sInstance == null) { - // Allow any objects being held to be gc'ed - mTextAfterCursor = null; - mTextBeforeCursor = null; - mExtractedText = null; - sInstance = this; - } - } - } - - @BinderThread - public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) { - synchronized (this) { - if (seq == mSeq) { - mTextBeforeCursor = textBeforeCursor; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setTextBeforeCursor, ignoring."); - } - } - } - - @BinderThread - public void setTextAfterCursor(CharSequence textAfterCursor, int seq) { - synchronized (this) { - if (seq == mSeq) { - mTextAfterCursor = textAfterCursor; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setTextAfterCursor, ignoring."); - } - } - } - - @BinderThread - public void setSelectedText(CharSequence selectedText, int seq) { - synchronized (this) { - if (seq == mSeq) { - mSelectedText = selectedText; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setSelectedText, ignoring."); - } - } - } - - @BinderThread - public void setCursorCapsMode(int capsMode, int seq) { - synchronized (this) { - if (seq == mSeq) { - mCursorCapsMode = capsMode; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setCursorCapsMode, ignoring."); - } - } - } - - @BinderThread - public void setExtractedText(ExtractedText extractedText, int seq) { - synchronized (this) { - if (seq == mSeq) { - mExtractedText = extractedText; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setExtractedText, ignoring."); - } - } - } - - @BinderThread - public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) { - synchronized (this) { - if (seq == mSeq) { - mRequestUpdateCursorAnchorInfoResult = result; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setCursorAnchorInfoRequestResult, ignoring."); - } - } - } - - @BinderThread - public void setCommitContentResult(boolean result, int seq) { - synchronized (this) { - if (seq == mSeq) { - mCommitContentResult = result; - mHaveValue = true; - notifyAll(); - } else { - Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq - + ") in setCommitContentResult, ignoring."); - } - } - } - - /** - * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds. - * - *The caller must be synchronized on this callback object.
- */
- @AnyThread
- void waitForResultLocked() {
- long startTime = SystemClock.uptimeMillis();
- long endTime = startTime + MAX_WAIT_TIME_MILLIS;
-
- while (!mHaveValue) {
- long remainingTime = endTime - SystemClock.uptimeMillis();
- if (remainingTime <= 0) {
- Log.w(TAG, "Timed out waiting on IInputContextCallback");
- return;
- }
- try {
- wait(remainingTime);
- } catch (InterruptedException e) {
- }
- }
- }
- }
+ private final CancellationGroup mCancellationGroup;
public InputConnectionWrapper(
@NonNull WeakReference