OSDN Git Service

Optimize focused input event dispatch in view root. DO NOT MERGE
authorJeff Brown <jeffbrown@google.com>
Mon, 14 Jan 2013 21:50:37 +0000 (13:50 -0800)
committerDake Gu <dake@google.com>
Mon, 14 Jan 2013 23:55:35 +0000 (15:55 -0800)
The efficiency of key, trackball and generic motion event
dispatch is greatly influenced by the IME dispatch cycle.
This change simplifies the dispatch of focused input events
and avoids causing event processing to be requeued on the
handler and delayed unnecessarily.

Bug: 7984576
Change-Id: Id82624a3f32c05efe6ee5c322bd55bf2ab21525d

core/java/android/view/ViewRootImpl.java
core/java/android/view/inputmethod/InputMethodManager.java

index fe2cf0e..9ef9d01 100644 (file)
@@ -232,6 +232,13 @@ public final class ViewRootImpl implements ViewParent,
     int mLastSystemUiVisibility;
     int mClientWindowLayoutFlags;
 
+    /** @hide */
+    public static final int EVENT_NOT_HANDLED = 0;
+    /** @hide */
+    public static final int EVENT_HANDLED = 1;
+    /** @hide */
+    public static final int EVENT_IN_PROGRESS = 2;
+
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
@@ -3157,19 +3164,37 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverInputEvent(QueuedInputEvent q) {
+    private int deliverInputEvent(QueuedInputEvent q) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
-                deliverKeyEvent(q);
+                return deliverKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    deliverPointerEvent(q);
+                    return deliverPointerEvent(q);
                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    deliverTrackballEvent(q);
+                    return deliverTrackballEvent(q);
+                } else {
+                    return deliverGenericMotionEvent(q);
+                }
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private int deliverInputEventPostIme(QueuedInputEvent q) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
+        try {
+            if (q.mEvent instanceof KeyEvent) {
+                return deliverKeyEventPostIme(q);
+            } else {
+                final int source = q.mEvent.getSource();
+                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return deliverTrackballEventPostIme(q);
                 } else {
-                    deliverGenericMotionEvent(q);
+                    return deliverGenericMotionEventPostIme(q);
                 }
             }
         } finally {
@@ -3177,7 +3202,7 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void deliverPointerEvent(QueuedInputEvent q) {
+    private int deliverPointerEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         final boolean isTouchEvent = event.isTouchEvent();
         if (mInputEventConsistencyVerifier != null) {
@@ -3190,8 +3215,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Translate the pointer event for compatibility, if needed.
@@ -3224,16 +3248,10 @@ public final class ViewRootImpl implements ViewParent,
         if (MEASURE_LATENCY) {
             lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
         }
-        if (handled) {
-            finishInputEvent(q, true);
-            return;
-        }
-
-        // Pointer event was unhandled.
-        finishInputEvent(q, false);
+        return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
     }
 
-    private void deliverTrackballEvent(QueuedInputEvent q) {
+    private int deliverTrackballEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
@@ -3252,24 +3270,25 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending trackball event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverTrackballEventPostIme(q);
+        return deliverTrackballEventPostIme(q);
     }
 
-    private void deliverTrackballEventPostIme(QueuedInputEvent q) {
+    private int deliverTrackballEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the trackball event to the view.
@@ -3279,10 +3298,8 @@ public final class ViewRootImpl implements ViewParent,
             // event into a key event, touch mode will not exit, so we exit
             // touch mode here.
             ensureTouchMode(false);
-
-            finishInputEvent(q, true);
             mLastTrackballTime = Integer.MIN_VALUE;
-            return;
+            return EVENT_HANDLED;
         }
 
         // Translate the trackball event into DPAD keys and try to deliver those.
@@ -3390,10 +3407,10 @@ public final class ViewRootImpl implements ViewParent,
 
         // Unfortunately we can't tell whether the application consumed the keys, so
         // we always consider the trackball event handled.
-        finishInputEvent(q, true);
+        return EVENT_HANDLED;
     }
 
-    private void deliverGenericMotionEvent(QueuedInputEvent q) {
+    private int deliverGenericMotionEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
@@ -3411,18 +3428,20 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending generic motion event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverGenericMotionEventPostIme(q);
+        return deliverGenericMotionEventPostIme(q);
     }
 
-    private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
+    private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
         final int source = event.getSource();
         final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
@@ -3436,8 +3455,7 @@ public final class ViewRootImpl implements ViewParent,
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the event to the view.
@@ -3448,22 +3466,21 @@ public final class ViewRootImpl implements ViewParent,
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         if (isJoystick) {
             // Translate the joystick event into DPAD keys and try to deliver
             // those.
             updateJoystickDirection(event, true);
-            finishInputEvent(q, true);
-        } else if (isTouchPad) {
+            return EVENT_HANDLED;
+        }
+        if (isTouchPad) {
             //Convert TouchPad motion into a TrackBall event
             mSimulatedTrackball.updateTrackballDirection(this, event);
-            finishInputEvent(q, true);
-        } else {
-            finishInputEvent(q, false);
+            return EVENT_HANDLED;
         }
+        return EVENT_NOT_HANDLED;
     }
 
     private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
@@ -3607,7 +3624,7 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverKeyEvent(QueuedInputEvent q) {
+    private int deliverKeyEvent(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
@@ -3618,8 +3635,7 @@ public final class ViewRootImpl implements ViewParent,
 
             // Perform predispatching before the IME.
             if (mView.dispatchKeyEventPreIme(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
@@ -3630,81 +3646,30 @@ public final class ViewRootImpl implements ViewParent,
                     final int seq = event.getSequenceNumber();
                     if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                             + seq + " event=" + event);
-                    imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
-                    return;
+                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                            mInputMethodCallback);
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverKeyEventPostIme(q);
-    }
-
-    void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        if (q != null && q.mEvent.getSequenceNumber() == seq) {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + " event=" + q);
-            }
-            if (handled) {
-                finishInputEvent(q, true);
-            } else {
-                if (q.mEvent instanceof KeyEvent) {
-                    KeyEvent event = (KeyEvent)q.mEvent;
-                    if (event.getAction() != KeyEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    deliverKeyEventPostIme(q);
-                } else {
-                    MotionEvent event = (MotionEvent)q.mEvent;
-                    if (event.getAction() != MotionEvent.ACTION_CANCEL
-                            && event.getAction() != MotionEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    final int source = q.mEvent.getSource();
-                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                        deliverTrackballEventPostIme(q);
-                    } else {
-                        deliverGenericMotionEventPostIme(q);
-                    }
-                }
-            }
-        } else {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + ", event not found!");
-            }
-        }
+        return deliverKeyEventPostIme(q);
     }
 
-    private void deliverKeyEventPostIme(QueuedInputEvent q) {
+    private int deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // If the key's purpose is to exit touch mode then we consume it and consider it handled.
         if (checkForLeavingTouchModeAndConsume(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Make sure the fallback event policy sees all keys that will be delivered to the
@@ -3713,8 +3678,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // Deliver the key to the view hierarchy.
         if (mView.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // If the Control modifier is held, try to interpret the key as a shortcut.
@@ -3723,15 +3687,13 @@ public final class ViewRootImpl implements ViewParent,
                 && event.getRepeatCount() == 0
                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
             if (mView.dispatchKeyShortcutEvent(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
         }
 
         // Apply the fallback event policy.
         if (mFallbackEventHandler.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Handle automatic focus changes.
@@ -3784,22 +3746,20 @@ public final class ViewRootImpl implements ViewParent,
                         if (v.requestFocus(direction, mTempRect)) {
                             playSoundEffect(SoundEffectConstants
                                     .getContantForFocusDirection(direction));
-                            finishInputEvent(q, true);
-                            return;
+                            return EVENT_HANDLED;
                         }
                     }
 
                     // Give the focused view a last chance to handle the dpad key.
                     if (mView.dispatchUnhandledMove(focused, direction)) {
-                        finishInputEvent(q, true);
-                        return;
+                        return EVENT_HANDLED;
                     }
                 }
             }
         }
 
         // Key was unhandled.
-        finishInputEvent(q, false);
+        return EVENT_NOT_HANDLED;
     }
 
     /* drag/drop */
@@ -4303,7 +4263,11 @@ public final class ViewRootImpl implements ViewParent,
             mFirstPendingInputEvent = q.mNext;
             q.mNext = null;
             mCurrentInputEvent = q;
-            deliverInputEvent(q);
+
+            final int result = deliverInputEvent(q);
+            if (result != EVENT_IN_PROGRESS) {
+                finishCurrentInputEvent(result == EVENT_HANDLED);
+            }
         }
 
         // We are done processing all input events that we can process right now
@@ -4314,10 +4278,42 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
-        if (q != mCurrentInputEvent) {
-            throw new IllegalStateException("finished input event out of order");
+    void handleImeFinishedEvent(int seq, boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        if (q != null && q.mEvent.getSequenceNumber() == seq) {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + " event=" + q);
+            }
+
+            if (!handled) {
+                // If the window doesn't currently have input focus, then drop
+                // this event.  This could be an event that came back from the
+                // IME dispatch but the window has lost focus in the meantime.
+                if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+                    Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                } else {
+                    final int result = deliverInputEventPostIme(q);
+                    if (result == EVENT_HANDLED) {
+                        handled = true;
+                    }
+                }
+            }
+            finishCurrentInputEvent(handled);
+
+            // Immediately start processing the next input event.
+            doProcessInputEvents();
+        } else {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + ", event not found!");
+            }
         }
+    }
+
+    private void finishCurrentInputEvent(boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        mCurrentInputEvent = null;
 
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -4326,10 +4322,18 @@ public final class ViewRootImpl implements ViewParent,
         }
 
         recycleQueuedInputEvent(q);
+    }
 
-        mCurrentInputEvent = null;
-        if (mFirstPendingInputEvent != null) {
-            scheduleProcessInputEvents();
+    private static boolean isTerminalInputEvent(InputEvent event) {
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            return keyEvent.getAction() == KeyEvent.ACTION_UP;
+        } else {
+            final MotionEvent motionEvent = (MotionEvent)event;
+            final int action = motionEvent.getAction();
+            return action == MotionEvent.ACTION_UP
+                    || action == MotionEvent.ACTION_CANCEL
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
         }
     }
 
index 4a3f846..d258f4d 100644 (file)
@@ -334,7 +334,7 @@ public final class InputMethodManager {
     
     class H extends Handler {
         H(Looper looper) {
-            super(looper);
+            super(looper, null, true);
         }
         
         @Override
@@ -1565,38 +1565,36 @@ public final class InputMethodManager {
     /**
      * @hide
      */
-    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
+    public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
             FinishedEventCallback callback) {
-        boolean handled = false;
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
 
             if (mCurMethod != null) {
                 if (key.getAction() == KeyEvent.ACTION_DOWN
-                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
+                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM
+                        && key.getRepeatCount() == 0) {
                     showInputMethodPickerLocked();
-                    handled = true;
-                } else {
-                    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;
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
-                    }
+                    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_IN_PROGRESS;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, handled);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
@@ -1607,20 +1605,19 @@ public final class InputMethodManager {
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
@@ -1631,14 +1628,13 @@ public final class InputMethodManager {
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     void finishedEvent(int seq, boolean handled) {