diff options
Diffstat (limited to 'android/inputmethodservice/IInputMethodWrapper.java')
-rw-r--r-- | android/inputmethodservice/IInputMethodWrapper.java | 93 |
1 files changed, 66 insertions, 27 deletions
diff --git a/android/inputmethodservice/IInputMethodWrapper.java b/android/inputmethodservice/IInputMethodWrapper.java index 765aff96..2c7e51a1 100644 --- a/android/inputmethodservice/IInputMethodWrapper.java +++ b/android/inputmethodservice/IInputMethodWrapper.java @@ -16,14 +16,8 @@ package android.inputmethodservice; -import com.android.internal.os.HandlerCaller; -import com.android.internal.os.SomeArgs; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethod; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.IInputSessionCallback; -import com.android.internal.view.InputConnectionWrapper; - +import android.annotation.BinderThread; +import android.annotation.MainThread; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; @@ -41,11 +35,20 @@ import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethod; +import com.android.internal.view.IInputMethodSession; +import com.android.internal.view.IInputSessionCallback; +import com.android.internal.view.InputConnectionWrapper; + import java.io.FileDescriptor; 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 @@ -67,17 +70,27 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_SHOW_SOFT_INPUT = 60; private static final int DO_HIDE_SOFT_INPUT = 70; private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80; - + final WeakReference<AbstractInputMethodService> mTarget; final Context mContext; final HandlerCaller mCaller; final WeakReference<InputMethod> mInputMethod; final int mTargetSdkVersion; - - static class Notifier { - boolean notified; - } - + + /** + * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()} + * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been + * called or not, mainly to avoid unnecessary blocking operations. + * + * <p>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 #unbindInput()} are called with the same order as the original calls + * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY} + * for detailed semantics.</p> + */ + AtomicBoolean mIsUnbindIssued = null; + // NOTE: we should have a cache of these. static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; @@ -108,20 +121,16 @@ class IInputMethodWrapper extends IInputMethod.Stub } } } - - public IInputMethodWrapper(AbstractInputMethodService context, - InputMethod inputMethod) { - mTarget = new WeakReference<AbstractInputMethodService>(context); + + public IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod) { + mTarget = new WeakReference<>(context); mContext = context.getApplicationContext(); mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/); - mInputMethod = new WeakReference<InputMethod>(inputMethod); + mInputMethod = new WeakReference<>(inputMethod); mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; } - public InputMethod getInternalInputMethod() { - return mInputMethod.get(); - } - + @MainThread @Override public void executeMessage(Message msg) { InputMethod inputMethod = mInputMethod.get(); @@ -169,8 +178,10 @@ 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 InputConnection ic = inputContext != null - ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; + ? new InputConnectionWrapper( + mTarget, inputContext, missingMethods, isUnbindIssued) : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */, startInputToken); @@ -205,6 +216,7 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Unhandled message code: " + msg.what); } + @BinderThread @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { AbstractInputMethodService target = mTarget.get(); @@ -232,40 +244,63 @@ class IInputMethodWrapper extends IInputMethod.Stub } } + @BinderThread @Override public void attachToken(IBinder token) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); } + @BinderThread @Override public void bindInput(InputBinding binding) { + if (mIsUnbindIssued != null) { + Log.e(TAG, "bindInput must be paired with unbindInput."); + } + mIsUnbindIssued = new AtomicBoolean(); // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; InputConnection ic = new InputConnectionWrapper(mTarget, - IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags); + IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags, + mIsUnbindIssued); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } + @BinderThread @Override public void unbindInput() { + if (mIsUnbindIssued != null) { + // Signal the flag then forget it. + mIsUnbindIssued.set(true); + mIsUnbindIssued = null; + } else { + Log.e(TAG, "unbindInput must be paired with bindInput."); + } mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } + @BinderThread @Override public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, EditorInfo attribute, boolean restarting) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT, - missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute)); + if (mIsUnbindIssued == null) { + Log.e(TAG, "startInput must be called after bindInput."); + mIsUnbindIssued = new AtomicBoolean(); + } + mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT, + missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute, + mIsUnbindIssued)); } + @BinderThread @Override public void createSession(InputChannel channel, IInputSessionCallback callback) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION, channel, callback)); } + @BinderThread @Override public void setSessionEnabled(IInputMethodSession session, boolean enabled) { try { @@ -282,6 +317,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } } + @BinderThread @Override public void revokeSession(IInputMethodSession session) { try { @@ -297,18 +333,21 @@ class IInputMethodWrapper extends IInputMethod.Stub } } + @BinderThread @Override public void showSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT, flags, resultReceiver)); } + @BinderThread @Override public void hideSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT, flags, resultReceiver)); } + @BinderThread @Override public void changeInputMethodSubtype(InputMethodSubtype subtype) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE, |