summaryrefslogtreecommitdiffstats
path: root/core/java/android/inputmethodservice
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2013-03-26 15:42:39 -0700
committerJeff Brown <jeffbrown@google.com>2013-03-26 15:42:39 -0700
commitc28867a1d67121ce5963de135e3ae2b1dbd9a33d (patch)
tree95da9070093882f0b95ee197f45f5b4a5e348d96 /core/java/android/inputmethodservice
parent37f180b4a52e4c1d0b6a7b400b6579b7ff25f307 (diff)
downloadframeworks_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/android/inputmethodservice')
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java5
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java170
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java52
3 files changed, 138 insertions, 89 deletions
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));