diff options
author | Jeff Brown <jeffbrown@google.com> | 2013-03-26 15:42:39 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2013-03-26 15:42:39 -0700 |
commit | c28867a1d67121ce5963de135e3ae2b1dbd9a33d (patch) | |
tree | 95da9070093882f0b95ee197f45f5b4a5e348d96 /core/java | |
parent | 37f180b4a52e4c1d0b6a7b400b6579b7ff25f307 (diff) | |
download | frameworks_base-c28867a1d67121ce5963de135e3ae2b1dbd9a33d.zip frameworks_base-c28867a1d67121ce5963de135e3ae2b1dbd9a33d.tar.gz frameworks_base-c28867a1d67121ce5963de135e3ae2b1dbd9a33d.tar.bz2 |
Use input transport for communications between app and IME.
The input method manager service now supplies an input channel for
communication while creating an IME session on behalf of the
application.
This change significanly reduces the overhead of IME event dispatch
by using a standard input channel to send input events rather than
using binder. This results in fewer thread context switches
and fewer object allocations.
What's more, the IME may perform additional batching of the motion
events that it receives which may help it catch up if it is
getting behind while processing them.
Bug: 7984576
Bug: 8473020
Change-Id: Ibe26311edd0060cdcae80194f1753482e635786f
Diffstat (limited to 'core/java')
12 files changed, 426 insertions, 273 deletions
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 396f910..51867bc 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -353,8 +353,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } void preDispatchKeyEvent(KeyEvent event, int seq) { - mIMM.dispatchKeyEvent(this, seq, event, - mInputMethodCallback); + mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback); } void setWindowFlags(int flags, int mask) { diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 3c3182a..3531926 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -126,11 +126,12 @@ public abstract class AbstractInputMethodService extends Service mRevoked = true; mEnabled = false; } - + /** * Take care of dispatching incoming key events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) { boolean handled = event.dispatch(AbstractInputMethodService.this, mDispatcherState, this); @@ -143,6 +144,7 @@ public abstract class AbstractInputMethodService extends Service * Take care of dispatching incoming trackball events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) { boolean handled = onTrackballEvent(event); if (callback != null) { @@ -154,6 +156,7 @@ public abstract class AbstractInputMethodService extends Service * Take care of dispatching incoming generic motion events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) { boolean handled = onGenericMotionEvent(event); if (callback != null) { diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index d78262b..726dcec 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -18,15 +18,20 @@ package android.inputmethodservice; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; @@ -36,14 +41,10 @@ import android.view.inputmethod.InputMethodSession; class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; - private static final boolean DEBUG = false; private static final int DO_FINISH_INPUT = 60; private static final int DO_DISPLAY_COMPLETIONS = 65; private static final int DO_UPDATE_EXTRACTED_TEXT = 67; - private static final int DO_DISPATCH_KEY_EVENT = 70; - private static final int DO_DISPATCH_TRACKBALL_EVENT = 80; - private static final int DO_DISPATCH_GENERIC_MOTION_EVENT = 85; private static final int DO_UPDATE_SELECTION = 90; private static final int DO_UPDATE_CURSOR = 95; private static final int DO_APP_PRIVATE_COMMAND = 100; @@ -53,34 +54,30 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub HandlerCaller mCaller; InputMethodSession mInputMethodSession; - - // NOTE: we should have a cache of these. - static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback { - final IInputMethodCallback mCb; - InputMethodEventCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - public void finishedEvent(int seq, boolean handled) { - try { - mCb.finishedEvent(seq, handled); - } catch (RemoteException e) { - } - } - } - + InputChannel mChannel; + ImeInputEventReceiver mReceiver; + public IInputMethodSessionWrapper(Context context, - InputMethodSession inputMethodSession) { + InputMethodSession inputMethodSession, InputChannel channel) { mCaller = new HandlerCaller(context, null, this, true /*asyncHandler*/); mInputMethodSession = inputMethodSession; + mChannel = channel; + if (channel != null) { + mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper()); + } } public InputMethodSession getInternalInputMethodSession() { return mInputMethodSession; } + @Override public void executeMessage(Message msg) { - if (mInputMethodSession == null) return; + if (mInputMethodSession == null) { + // The session has been finished. + return; + } switch (msg.what) { case DO_FINISH_INPUT: @@ -93,33 +90,6 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.updateExtractedText(msg.arg1, (ExtractedText)msg.obj); return; - case DO_DISPATCH_KEY_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchKeyEvent(msg.arg1, - (KeyEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } - case DO_DISPATCH_TRACKBALL_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchTrackballEvent(msg.arg1, - (MotionEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } - case DO_DISPATCH_GENERIC_MOTION_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchGenericMotionEvent(msg.arg1, - (MotionEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } case DO_UPDATE_SELECTION: { SomeArgs args = (SomeArgs)msg.obj; mInputMethodSession.updateSelection(args.argi1, args.argi2, @@ -143,7 +113,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub return; } case DO_FINISH_SESSION: { - mInputMethodSession = null; + doFinishSession(); return; } case DO_VIEW_CLICKED: { @@ -153,37 +123,37 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } Log.w(TAG, "Unhandled message code: " + msg.what); } - + + private void doFinishSession() { + mInputMethodSession = null; + if (mReceiver != null) { + mReceiver.dispose(); + mReceiver = null; + } + if (mChannel != null) { + mChannel.dispose(); + mChannel = null; + } + } + + @Override public void finishInput() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT)); } + @Override public void displayCompletions(CompletionInfo[] completions) { mCaller.executeOrSendMessage(mCaller.obtainMessageO( DO_DISPLAY_COMPLETIONS, completions)); } - + + @Override public void updateExtractedText(int token, ExtractedText text) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO( DO_UPDATE_EXTRACTED_TEXT, token, text)); } - - public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq, - event, callback)); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq, - event, callback)); - } - - public void dispatchGenericMotionEvent(int seq, MotionEvent event, - IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq, - event, callback)); - } + @Override public void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION, @@ -191,24 +161,74 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub candidatesStart, candidatesEnd)); } + @Override public void viewClicked(boolean focusChanged) { - mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); } + @Override public void updateCursor(Rect newCursor) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR, - newCursor)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor)); } - + + @Override public void appPrivateCommand(String action, Bundle data) { - mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); } - + + @Override public void toggleSoftInput(int showFlags, int hideFlags) { - mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); } + @Override public void finishSession() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION)); } + + private final class ImeInputEventReceiver extends InputEventReceiver + implements InputMethodSession.EventCallback { + private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>(); + + public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEvent(InputEvent event) { + if (mInputMethodSession == null) { + // The session has been finished. + finishInputEvent(event, false); + return; + } + + final int seq = event.getSequenceNumber(); + mPendingEvents.put(seq, event); + if (event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent)event; + mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this); + } else { + MotionEvent motionEvent = (MotionEvent)event; + if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) { + mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this); + } else { + mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this); + } + } + } + + @Override + public void finishedEvent(int seq, boolean handled) { + int index = mPendingEvents.indexOfKey(seq); + if (index >= 0) { + InputEvent event = mPendingEvents.valueAt(index); + mPendingEvents.removeAt(index); + finishInputEvent(event, handled); + } + } + } } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 0d981be..9306373 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -32,6 +32,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; +import android.view.InputChannel; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputConnection; @@ -53,7 +54,6 @@ import java.util.concurrent.TimeUnit; class IInputMethodWrapper extends IInputMethod.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; - private static final boolean DEBUG = false; private static final int DO_DUMP = 1; private static final int DO_ATTACH_TOKEN = 10; @@ -78,20 +78,29 @@ class IInputMethodWrapper extends IInputMethod.Stub } // NOTE: we should have a cache of these. - static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { + static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; + final InputChannel mChannel; final IInputSessionCallback mCb; - InputMethodSessionCallbackWrapper(Context context, IInputSessionCallback cb) { + + InputMethodSessionCallbackWrapper(Context context, InputChannel channel, + IInputSessionCallback cb) { mContext = context; + mChannel = channel; mCb = cb; } + + @Override public void sessionCreated(InputMethodSession session) { try { if (session != null) { IInputMethodSessionWrapper wrap = - new IInputMethodSessionWrapper(mContext, session); + new IInputMethodSessionWrapper(mContext, session, mChannel); mCb.sessionCreated(wrap); } else { + if (mChannel != null) { + mChannel.dispose(); + } mCb.sessionCreated(null); } } catch (RemoteException e) { @@ -112,6 +121,7 @@ class IInputMethodWrapper extends IInputMethod.Stub return mInputMethod.get(); } + @Override public void executeMessage(Message msg) { InputMethod inputMethod = mInputMethod.get(); // Need a valid reference to the inputMethod for everything except a dump. @@ -174,8 +184,11 @@ class IInputMethodWrapper extends IInputMethod.Stub return; } case DO_CREATE_SESSION: { + SomeArgs args = (SomeArgs)msg.obj; inputMethod.createSession(new InputMethodSessionCallbackWrapper( - mCaller.mContext, (IInputSessionCallback)msg.obj)); + mCaller.mContext, (InputChannel)args.arg1, + (IInputSessionCallback)args.arg2)); + args.recycle(); return; } case DO_SET_SESSION_ENABLED: @@ -197,8 +210,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } Log.w(TAG, "Unhandled message code: " + msg.what); } - - @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + + @Override + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { AbstractInputMethodService target = mTarget.get(); if (target == null) { return; @@ -224,10 +238,12 @@ class IInputMethodWrapper extends IInputMethod.Stub } } + @Override public void attachToken(IBinder token) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); } - + + @Override public void bindInput(InputBinding binding) { InputConnection ic = new InputConnectionWrapper( IInputContext.Stub.asInterface(binding.getConnectionToken())); @@ -235,24 +251,30 @@ class IInputMethodWrapper extends IInputMethod.Stub mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } + @Override public void unbindInput() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } + @Override public void startInput(IInputContext inputContext, EditorInfo attribute) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, inputContext, attribute)); } + @Override public void restartInput(IInputContext inputContext, EditorInfo attribute) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT, inputContext, attribute)); } - public void createSession(IInputSessionCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback)); + @Override + public void createSession(InputChannel channel, IInputSessionCallback callback) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION, + channel, callback)); } + @Override public void setSessionEnabled(IInputMethodSession session, boolean enabled) { try { InputMethodSession ls = ((IInputMethodSessionWrapper) @@ -263,7 +285,8 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Incoming session not of correct type: " + session, e); } } - + + @Override public void revokeSession(IInputMethodSession session) { try { InputMethodSession ls = ((IInputMethodSessionWrapper) @@ -273,17 +296,20 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Incoming session not of correct type: " + session, e); } } - + + @Override public void showSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT, flags, resultReceiver)); } - + + @Override public void hideSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT, flags, resultReceiver)); } + @Override public void changeInputMethodSubtype(InputMethodSubtype subtype) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE, subtype)); diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index 523af04..a797176 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -78,7 +78,9 @@ public final class InputChannel implements Parcelable { * Creates a new input channel pair. One channel should be provided to the input * dispatcher and the other to the application's input queue. * @param name The descriptive (non-unique) name of the channel pair. - * @return A pair of input channels. They are symmetric and indistinguishable. + * @return A pair of input channels. The first channel is designated as the + * server channel and should be used to publish input events. The second channel + * is designated as the client channel and should be used to consume input events. */ public static InputChannel[] openInputChannelPair(String name) { if (name == null) { @@ -123,10 +125,11 @@ public final class InputChannel implements Parcelable { nativeTransferTo(outParameter); } + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; } - + public void readFromParcel(Parcel in) { if (in == null) { throw new IllegalArgumentException("in must not be null"); @@ -134,7 +137,8 @@ public final class InputChannel implements Parcelable { nativeReadFromParcel(in); } - + + @Override public void writeToParcel(Parcel out, int flags) { if (out == null) { throw new IllegalArgumentException("out must not be null"); diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java new file mode 100644 index 0000000..adf63fe --- /dev/null +++ b/core/java/android/view/InputEventSender.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 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 android.view; + +import dalvik.system.CloseGuard; + +import android.os.Looper; +import android.os.MessageQueue; +import android.util.Log; + +/** + * Provides a low-level mechanism for an application to send input events. + * @hide + */ +public abstract class InputEventSender { + private static final String TAG = "InputEventSender"; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private int mSenderPtr; + + // We keep references to the input channel and message queue objects here so that + // they are not GC'd while the native peer of the receiver is using them. + private InputChannel mInputChannel; + private MessageQueue mMessageQueue; + + private static native int nativeInit(InputEventSender sender, + InputChannel inputChannel, MessageQueue messageQueue); + private static native void nativeDispose(int senderPtr); + private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event); + private static native boolean nativeSendMotionEvent(int senderPtr, int seq, MotionEvent event); + + /** + * Creates an input event sender bound to the specified input channel. + * + * @param inputChannel The input channel. + * @param looper The looper to use when invoking callbacks. + */ + public InputEventSender(InputChannel inputChannel, Looper looper) { + if (inputChannel == null) { + throw new IllegalArgumentException("inputChannel must not be null"); + } + if (looper == null) { + throw new IllegalArgumentException("looper must not be null"); + } + + mInputChannel = inputChannel; + mMessageQueue = looper.getQueue(); + mSenderPtr = nativeInit(this, inputChannel, mMessageQueue); + + mCloseGuard.open("dispose"); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(true); + } finally { + super.finalize(); + } + } + + /** + * Disposes the receiver. + */ + public void dispose() { + dispose(false); + } + + private void dispose(boolean finalized) { + if (mCloseGuard != null) { + if (finalized) { + mCloseGuard.warnIfOpen(); + } + mCloseGuard.close(); + } + + if (mSenderPtr != 0) { + nativeDispose(mSenderPtr); + mSenderPtr = 0; + } + mInputChannel = null; + mMessageQueue = null; + } + + /** + * Called when an input event is finished. + * + * @param seq The input event sequence number. + * @param handled True if the input event was handled. + */ + public void onInputEventFinished(int seq, boolean handled) { + } + + /** + * Sends an input event. + * Must be called on the same Looper thread to which the sender is attached. + * + * @param seq The input event sequence number. + * @param event The input event to send. + * @return True if the entire event was sent successfully. May return false + * if the input channel buffer filled before all samples were dispatched. + */ + public final boolean sendInputEvent(int seq, InputEvent event) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (mSenderPtr == 0) { + Log.w(TAG, "Attempted to send an input event but the input event " + + "sender has already been disposed."); + return false; + } + + if (event instanceof KeyEvent) { + return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event); + } else { + return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event); + } + } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchInputEventFinished(int seq, boolean handled) { + onInputEventFinished(seq, handled); + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b30dc83..b228704 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2937,7 +2937,6 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_DISPATCH_KEY = 7; private final static int MSG_DISPATCH_APP_VISIBILITY = 8; private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; - private final static int MSG_IME_FINISHED_EVENT = 10; private final static int MSG_DISPATCH_KEY_FROM_IME = 11; private final static int MSG_FINISH_INPUT_CONNECTION = 12; private final static int MSG_CHECK_FOCUS = 13; @@ -2975,8 +2974,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_DISPATCH_APP_VISIBILITY"; case MSG_DISPATCH_GET_NEW_SURFACE: return "MSG_DISPATCH_GET_NEW_SURFACE"; - case MSG_IME_FINISHED_EVENT: - return "MSG_IME_FINISHED_EVENT"; case MSG_DISPATCH_KEY_FROM_IME: return "MSG_DISPATCH_KEY_FROM_IME"; case MSG_FINISH_INPUT_CONNECTION: @@ -3018,9 +3015,6 @@ public final class ViewRootImpl implements ViewParent, info.target.invalidate(info.left, info.top, info.right, info.bottom); info.recycle(); break; - case MSG_IME_FINISHED_EVENT: - handleImeFinishedEvent(msg.arg1, msg.arg2 != 0); - break; case MSG_PROCESS_INPUT_EVENTS: mProcessInputEventsScheduled = false; doProcessInputEvents(); @@ -3444,26 +3438,15 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onTrackballEvent(event, 0); } + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching trackball " + event + " to " + mView); // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) - Log.v(TAG, "Sending trackball event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchTrackballEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverTrackballEventPostIme(QueuedInputEvent q) { @@ -3598,26 +3581,16 @@ public final class ViewRootImpl implements ViewParent, if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } + + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching generic motion " + event + " to " + mView); // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) - Log.v(TAG, "Sending generic motion event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchGenericMotionEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverGenericMotionEventPostIme(QueuedInputEvent q) { @@ -3806,6 +3779,7 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onKeyEvent(event, 0); } + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); @@ -3815,20 +3789,9 @@ public final class ViewRootImpl implements ViewParent, } // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchKeyEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverKeyEventPostIme(QueuedInputEvent q) { @@ -4317,14 +4280,6 @@ public final class ViewRootImpl implements ViewParent, } } - void dispatchImeFinishedEvent(int seq, boolean handled) { - Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT); - msg.arg1 = seq; - msg.arg2 = handled ? 1 : 0; - msg.setAsynchronous(true); - mHandler.sendMessage(msg); - } - public void dispatchFinishInputConnection(InputConnection connection) { Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection); mHandler.sendMessage(msg); @@ -4533,6 +4488,21 @@ public final class ViewRootImpl implements ViewParent, return q; } + int dispatchImeInputEvent(QueuedInputEvent q) { + if (mLastWasImTarget) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + final InputEvent event = q.mEvent; + final int seq = event.getSequenceNumber(); + if (DEBUG_IMF) + Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event); + return imm.dispatchInputEvent(mView.getContext(), seq, event, + mInputMethodCallback); + } + } + return EVENT_POST_IME; + } + void handleImeFinishedEvent(int seq, boolean handled) { QueuedInputEvent q = mCurrentInputEventHead; if (q != null && q.mEvent.getSequenceNumber() == seq) { @@ -5132,7 +5102,7 @@ public final class ViewRootImpl implements ViewParent, public void finishedEvent(int seq, boolean handled) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchImeFinishedEvent(seq, handled); + viewAncestor.handleImeFinishedEvent(seq, handled); } } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index e602eb7..4207832 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -19,7 +19,6 @@ package android.view.inputmethod; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; @@ -40,8 +39,10 @@ import android.text.style.SuggestionSpan; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventSender; import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.ViewRootImpl; @@ -319,6 +320,8 @@ public final class InputMethodManager { * The actual instance of the method to make calls on it. */ IInputMethodSession mCurMethod; + InputChannel mCurChannel; + ImeInputEventSender mCurSender; PendingEvent mPendingEventPool; int mPendingEventPoolSize; @@ -363,10 +366,17 @@ public final class InputMethodManager { if (mBindSequence < 0 || mBindSequence != res.sequence) { Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence + ", given seq=" + res.sequence); + if (res.channel != null) { + res.channel.dispose(); + } return; } mCurMethod = res.method; + if (mCurChannel != null) { + mCurChannel.dispose(); + } + mCurChannel = res.channel; mCurId = res.id; mBindSequence = res.sequence; } @@ -482,10 +492,10 @@ public final class InputMethodManager { } final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { - @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + @Override + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { // No need to check for dump permission, since we only give this // interface to the system. - CountDownLatch latch = new CountDownLatch(1); SomeArgs sargs = SomeArgs.obtain(); sargs.arg1 = fd; @@ -501,32 +511,29 @@ public final class InputMethodManager { fout.println("Interrupted waiting for dump"); } } - + + @Override public void setUsingInputMethod(boolean state) { } - + + @Override public void onBindMethod(InputBindResult res) { mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); } - + + @Override public void onUnbindMethod(int sequence) { mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0)); } - + + @Override public void setActive(boolean active) { mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0)); } - }; - + }; + final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); - final IInputMethodCallback mInputMethodCallback = new IInputMethodCallback.Stub() { - @Override - public void finishedEvent(int seq, boolean handled) { - InputMethodManager.this.finishedEvent(seq, handled); - } - }; - InputMethodManager(IInputMethodManager service, Looper looper) { mService = service; mMainLooper = looper; @@ -714,6 +721,14 @@ public final class InputMethodManager { mBindSequence = -1; mCurId = null; mCurMethod = null; + if (mCurSender != null) { + mCurSender.dispose(); + mCurSender = null; + } + if (mCurChannel != null) { + mCurChannel.dispose(); + mCurChannel = null; + } } /** @@ -1085,6 +1100,7 @@ public final class InputMethodManager { // we need to reschedule our work for over there. if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); vh.post(new Runnable() { + @Override public void run() { startInputInner(null, 0, 0, 0); } @@ -1158,11 +1174,20 @@ public final class InputMethodManager { if (res.id != null) { mBindSequence = res.sequence; mCurMethod = res.method; + if (mCurChannel != null) { + mCurChannel.dispose(); + } + mCurChannel = res.channel; mCurId = res.id; - } else if (mCurMethod == null) { - // This means there is no input method available. - if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); - return true; + } else { + if (res.channel != null) { + res.channel.dispose(); + } + if (mCurMethod == null) { + // This means there is no input method available. + if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); + return true; + } } } if (mCurMethod != null && mCompletions != null) { @@ -1556,76 +1581,39 @@ public final class InputMethodManager { throw new RuntimeException(e); } } - - /** - * @hide - */ - public int dispatchKeyEvent(Context context, int seq, KeyEvent key, - FinishedEventCallback callback) { - synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); - - if (mCurMethod != null) { - if (key.getAction() == KeyEvent.ACTION_DOWN - && key.getKeyCode() == KeyEvent.KEYCODE_SYM - && key.getRepeatCount() == 0) { - showInputMethodPickerLocked(); - return ViewRootImpl.EVENT_HANDLED; - } - try { - if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); - } - } - } - return ViewRootImpl.EVENT_POST_IME; - } /** * @hide */ - public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion, + public int dispatchInputEvent(Context context, int seq, InputEvent event, FinishedEventCallback callback) { synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); + if (DEBUG) Log.d(TAG, "dispatchInputEvent"); - if (mCurMethod != null && mCurrentTextBoxAttribute != null) { - try { - if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); + if (mCurMethod != null) { + if (event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent)event; + if (keyEvent.getAction() == KeyEvent.ACTION_DOWN + && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM + && keyEvent.getRepeatCount() == 0) { + showInputMethodPickerLocked(); + return ViewRootImpl.EVENT_HANDLED; + } } - } - } - return ViewRootImpl.EVENT_POST_IME; - } - /** - * @hide - */ - public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion, - FinishedEventCallback callback) { - synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent"); - - if (mCurMethod != null && mCurrentTextBoxAttribute != null) { - try { - if (DEBUG) Log.v(TAG, "DISPATCH GENERIC MOTION: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e); + if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod); + final long startTime = SystemClock.uptimeMillis(); + if (mCurChannel != null) { + if (mCurSender == null) { + mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper()); + } + if (mCurSender.sendInputEvent(seq, event)) { + enqueuePendingEventLocked(startTime, seq, mCurId, callback); + return ViewRootImpl.EVENT_PENDING_IME; + } else { + Log.w(TAG, "Unable to send input event to IME: " + + mCurId + " dropping: " + event); + } } } } @@ -1937,6 +1925,17 @@ public final class InputMethodManager { public void finishedEvent(int seq, boolean handled); } + private final class ImeInputEventSender extends InputEventSender { + public ImeInputEventSender(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEventFinished(int seq, boolean handled) { + finishedEvent(seq, handled); + } + } + private static final class PendingEvent { public PendingEvent mNext; diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index bd947e9..77456da 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -18,6 +18,7 @@ package com.android.internal.view; import android.os.IBinder; import android.os.ResultReceiver; +import android.view.InputChannel; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethodSubtype; @@ -41,7 +42,7 @@ oneway interface IInputMethod { void restartInput(in IInputContext inputContext, in EditorInfo attribute); - void createSession(IInputSessionCallback callback); + void createSession(in InputChannel channel, IInputSessionCallback callback); void setSessionEnabled(IInputMethodSession session, boolean enabled); diff --git a/core/java/com/android/internal/view/IInputMethodCallback.aidl b/core/java/com/android/internal/view/IInputMethodCallback.aidl deleted file mode 100644 index 717a82d..0000000 --- a/core/java/com/android/internal/view/IInputMethodCallback.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2008 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.view; - -/** - * Helper interface for IInputMethod to allow the input method to call back - * to its client with results from incoming calls. - * {@hide} - */ -oneway interface IInputMethodCallback { - void finishedEvent(int seq, boolean handled); -} diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index cdec254..90210ce 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -22,7 +22,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; -import com.android.internal.view.IInputMethodCallback; /** * Sub-interface of IInputMethod which is safe to give to client applications. @@ -40,14 +39,8 @@ oneway interface IInputMethodSession { void viewClicked(boolean focusChanged); void updateCursor(in Rect newCursor); - - void displayCompletions(in CompletionInfo[] completions); - - void dispatchKeyEvent(int seq, in KeyEvent event, IInputMethodCallback callback); - void dispatchTrackballEvent(int seq, in MotionEvent event, IInputMethodCallback callback); - - void dispatchGenericMotionEvent(int seq, in MotionEvent event, IInputMethodCallback callback); + void displayCompletions(in CompletionInfo[] completions); void appPrivateCommand(String action, in Bundle data); diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 658f098..9143c61 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -18,6 +18,7 @@ package com.android.internal.view; import android.os.Parcel; import android.os.Parcelable; +import android.view.InputChannel; /** * Bundle of information returned by input method manager about a successful @@ -30,7 +31,12 @@ public final class InputBindResult implements Parcelable { * The input method service. */ public final IInputMethodSession method; - + + /** + * The input channel used to send input events to this IME. + */ + public final InputChannel channel; + /** * The ID for this input method, as found in InputMethodInfo; null if * no input method will be bound. @@ -42,18 +48,25 @@ public final class InputBindResult implements Parcelable { */ public final int sequence; - public InputBindResult(IInputMethodSession _method, String _id, int _sequence) { + public InputBindResult(IInputMethodSession _method, InputChannel _channel, + String _id, int _sequence) { method = _method; + channel = _channel; id = _id; sequence = _sequence; } InputBindResult(Parcel source) { method = IInputMethodSession.Stub.asInterface(source.readStrongBinder()); + if (source.readInt() != 0) { + channel = InputChannel.CREATOR.createFromParcel(source); + } else { + channel = null; + } id = source.readString(); sequence = source.readInt(); } - + @Override public String toString() { return "InputBindResult{" + method + " " + id @@ -62,12 +75,19 @@ public final class InputBindResult implements Parcelable { /** * Used to package this object into a {@link Parcel}. - * + * * @param dest The {@link Parcel} to be written. * @param flags The flags used for parceling. */ + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeStrongInterface(method); + if (channel != null) { + dest.writeInt(1); + channel.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } dest.writeString(id); dest.writeInt(sequence); } @@ -75,17 +95,21 @@ public final class InputBindResult implements Parcelable { /** * Used to make this class parcelable. */ - public static final Parcelable.Creator<InputBindResult> CREATOR = new Parcelable.Creator<InputBindResult>() { + public static final Parcelable.Creator<InputBindResult> CREATOR = + new Parcelable.Creator<InputBindResult>() { + @Override public InputBindResult createFromParcel(Parcel source) { return new InputBindResult(source); } + @Override public InputBindResult[] newArray(int size) { return new InputBindResult[size]; } }; + @Override public int describeContents() { - return 0; + return channel != null ? channel.describeContents() : 0; } } |