More native input event dispatching.

Added ANRs handling.
Added event injection.
Fixed a NPE ActivityManagerServer writing ANRs to the drop box.
Fixed HOME key interception.
Fixed trackball reporting.
Fixed pointer rotation in landscape mode.

Change-Id: I50340f559f22899ab924e220a78119ffc79469b7
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 72c4166..8d9bb29 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -81,6 +81,10 @@
     private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(InputChannel inputChannel);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+    private static native int nativeInjectKeyEvent(KeyEvent event, int nature,
+            int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+    private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
+            int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
     
     // Device class as defined by EventHub.
     private static final int CLASS_KEYBOARD = 0x00000001;
@@ -90,6 +94,12 @@
     private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
     private static final int CLASS_DPAD = 0x00000020;
     
+    // Input event injection constants defined in InputDispatcher.h.
+    static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
+    static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
+    static final int INPUT_EVENT_INJECTION_FAILED = 2;
+    static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
+    
     public InputManager(Context context,
             WindowManagerService windowManagerService,
             WindowManagerPolicy windowManagerPolicy,
@@ -215,37 +225,63 @@
         nativeUnregisterInputChannel(inputChannel);
     }
     
-    // TBD where this really belongs, duplicate copy in WindowManagerService
-    static final int INJECT_FAILED = 0;
-    static final int INJECT_SUCCEEDED = 1;
-    static final int INJECT_NO_PERMISSION = -1;
-    
     /**
      * Injects a key event into the event system on behalf of an application.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
      * @param event The event to inject.
      * @param nature The nature of the event.
+     * @param injectorPid The pid of the injecting application.
+     * @param injectorUid The uid of the injecting application.
      * @param sync If true, waits for the event to be completed before returning.
-     * @param pid The pid of the injecting application.
-     * @param uid The uid of the injecting application.
-     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     * @param timeoutMillis The injection timeout in milliseconds.
+     * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
-        // TODO
-        return INJECT_FAILED;
+    public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid,
+            boolean sync, int timeoutMillis) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (injectorPid < 0 || injectorUid < 0) {
+            throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+        }
+        if (timeoutMillis <= 0) {
+            throw new IllegalArgumentException("timeoutMillis must be positive");
+        }
+        
+        return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid,
+                sync, timeoutMillis);
     }
     
     /**
      * Injects a motion event into the event system on behalf of an application.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
      * @param event The event to inject.
      * @param nature The nature of the event.
      * @param sync If true, waits for the event to be completed before returning.
-     * @param pid The pid of the injecting application.
-     * @param uid The uid of the injecting application.
-     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     * @param injectorPid The pid of the injecting application.
+     * @param injectorUid The uid of the injecting application.
+     * @param sync If true, waits for the event to be completed before returning.
+     * @param timeoutMillis The injection timeout in milliseconds.
+     * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
-        // TODO
-        return INJECT_FAILED;
+    public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid,
+            boolean sync, int timeoutMillis) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (injectorPid < 0 || injectorUid < 0) {
+            throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+        }
+        if (timeoutMillis <= 0) {
+            throw new IllegalArgumentException("timeoutMillis must be positive");
+        }
+        
+        return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid,
+                sync, timeoutMillis);
     }
     
     public void dump(PrintWriter pw) {
@@ -271,8 +307,6 @@
         private static final boolean DEBUG_VIRTUAL_KEYS = false;
         private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
         
-        private final InputTargetList mReusableInputTargetList = new InputTargetList();
-        
         @SuppressWarnings("unused")
         public boolean isScreenOn() {
             return mPowerManagerService.isScreenOn();
@@ -309,6 +343,21 @@
         }
         
         @SuppressWarnings("unused")
+        public void notifyInputChannelBroken(InputChannel inputChannel) {
+            mWindowManagerService.notifyInputChannelBroken(inputChannel);
+        }
+
+        @SuppressWarnings("unused")
+        public long notifyInputChannelANR(InputChannel inputChannel) {
+            return mWindowManagerService.notifyInputChannelANR(inputChannel);
+        }
+
+        @SuppressWarnings("unused")
+        public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+            mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
+        }
+        
+        @SuppressWarnings("unused")
         public int hackInterceptKey(int deviceId, int type, int scanCode,
                 int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
             RawInputEvent event = new RawInputEvent();
@@ -437,24 +486,23 @@
             return names.toArray(new String[names.size()]);
         }
         
+        // TODO All code related to target identification should be moved down into native.
         @SuppressWarnings("unused")
-        public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
-            mReusableInputTargetList.clear();
-            
-            mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
-                    event, nature, policyFlags);
-            
-            return mReusableInputTargetList.toNullTerminatedArray();
+        public int getKeyEventTargets(InputTargetList inputTargets,
+                KeyEvent event, int nature, int policyFlags,
+                int injectorPid, int injectorUid) {
+            inputTargets.clear();
+            return mWindowManagerService.getKeyEventTargetsTd(
+                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
         }
         
         @SuppressWarnings("unused")
-        public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
-            mReusableInputTargetList.clear();
-            
-            mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
-                    event, nature, policyFlags);
-            
-            return mReusableInputTargetList.toNullTerminatedArray();
+        public int getMotionEventTargets(InputTargetList inputTargets,
+                MotionEvent event, int nature, int policyFlags,
+                int injectorPid, int injectorUid) {
+            inputTargets.clear();
+            return mWindowManagerService.getMotionEventTargetsTd(
+                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
         }
     }
 }
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
index 1575612..83acc8f 100644
--- a/services/java/com/android/server/InputTargetList.java
+++ b/services/java/com/android/server/InputTargetList.java
@@ -29,7 +29,7 @@
  * 
  * @hide
  */
-public class InputTargetList {
+public final class InputTargetList {
     private InputTarget[] mArray;
     private int mCount;
     
@@ -55,7 +55,7 @@
             count -= 1;
             mArray[count].recycle();
         }
-        // mArray[0] could be set to null here but we do it in toNullTerminatedArray()
+        mArray[0] = null;
     }
     
     /**
@@ -91,7 +91,7 @@
         
         mArray[mCount] = inputTarget;
         mCount += 1;
-        // mArray[mCount] could be set to null here but we do it in toNullTerminatedArray()
+        mArray[mCount] = null;
     }
     
     /**
@@ -99,7 +99,6 @@
      * @return The input target array.
      */
     public InputTarget[] toNullTerminatedArray() {
-        mArray[mCount] = null;
         return mArray;
     }
 }
\ No newline at end of file
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9bc3931..95ab5bc 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -203,6 +203,10 @@
     /** Adjustment to time to perform a dim, to make it more dramatic.
      */
     static final int DIM_DURATION_MULTIPLIER = 6;
+    
+    // Maximum number of milliseconds to wait for input event injection.
+    // FIXME is this value reasonable?
+    private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
 
     static final int INJECT_FAILED = 0;
     static final int INJECT_SUCCEEDED = 1;
@@ -447,8 +451,6 @@
     final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
     final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
 
-    //flag to detect fat touch events
-    boolean mFatTouch = false;
     Display mDisplay;
 
     H mH = new H();
@@ -5072,106 +5074,336 @@
         mPolicy.adjustConfigurationLw(config);
         return true;
     }
+    
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public void notifyInputChannelBroken(InputChannel inputChannel) {
+        synchronized (mWindowMap) {
+            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+            if (windowState == null) {
+                return; // irrelevant
+            }
+            
+            Slog.i(TAG, "WINDOW DIED " + windowState);
+            removeWindowLocked(windowState.mSession, windowState);
+        }
+    }
+    
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public long notifyInputChannelANR(InputChannel inputChannel) {
+        IApplicationToken appToken;
+        synchronized (mWindowMap) {
+            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+            if (windowState == null) {
+                return -2; // irrelevant, abort dispatching (-2)
+            }
+            
+            Slog.i(TAG, "Input event dispatching timed out sending to "
+                    + windowState.mAttrs.getTitle());
+            appToken = windowState.getAppToken();
+        }
+        
+        try {
+            // Notify the activity manager about the timeout and let it decide whether
+            // to abort dispatching or keep waiting.
+            boolean abort = appToken.keyDispatchingTimedOut();
+            if (abort) {
+                return -2; // abort dispatching
+            }
+            
+            // Return new timeout.
+            // We use -1 for infinite timeout to avoid clash with -2 magic number.
+            long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000;
+            return newTimeout < 0 ? -1 : newTimeout;
+        } catch (RemoteException ex) {
+            return -2; // abort dispatching
+        }
+    }
+
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+        // Nothing to do just now.
+        // Just wait for the user to dismiss the ANR dialog.
+        
+        // TODO We could try to automatically dismiss the ANR dialog on recovery
+        //      although that might be disorienting.
+    }
+    
+    private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+        int windowCount = mWindows.size();
+        for (int i = 0; i < windowCount; i++) {
+            WindowState windowState = (WindowState) mWindows.get(i);
+            if (windowState.mInputChannel == inputChannel) {
+                return windowState;
+            }
+        }
+        
+        return null;
+    }
 
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
     
-    public void getKeyEventTargets(InputTargetList inputTargets,
-            KeyEvent event, int nature, int policyFlags) {
+    private boolean checkInjectionPermissionTd(WindowState focus,
+            int injectorPid, int injectorUid) {
+        if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) {
+            if (mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission denied: injecting key event from pid "
+                        + injectorPid + " uid " + injectorUid + " to window " + focus
+                        + " owned by uid " + focus.mSession.mUid);
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    /* Gets the input targets for a key event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    public int getKeyEventTargetsTd(InputTargetList inputTargets,
+            KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
         if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
 
         // TODO what do we do with mDisplayFrozen?
         // TODO what do we do with focus.mToken.paused?
         
         WindowState focus = getFocusedWindow();
-        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
         
-        addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
-    }
-    
-    // Target of Motion events
-    WindowState mTouchFocus;
-
-    // Windows above the target who would like to receive an "outside"
-    // touch event for any down events outside of them.
-    // (This is a linked list by way of WindowState.mNextOutsideTouch.)
-    WindowState mOutsideTouchTargets;
-    
-    private void clearTouchFocus() {
-        mTouchFocus = null;
-        mOutsideTouchTargets = null;
-    }
-    
-    public void getMotionEventTargets(InputTargetList inputTargets,
-            MotionEvent event, int nature, int policyFlags) {
-        if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) {
-            // More or less the same as for keys...
-            WindowState focus = getFocusedWindow();
-            wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-            
-            addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
-            return;
+        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
+            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         }
         
-        int action = event.getAction();
+        if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(),
+                event.getAction() == KeyEvent.ACTION_DOWN,
+                event.getRepeatCount(), event.getFlags())) {
+            // Policy consumed the event.
+            return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+        }
+        
+        if (focus == null) {
+            return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+        
+        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+        
+        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
+        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+    }
+    
+    /* Gets the input targets for a motion event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    public int getMotionEventTargetsTd(InputTargetList inputTargets,
+            MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
+        switch (nature) {
+            case InputQueue.INPUT_EVENT_NATURE_TRACKBALL:
+                return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags,
+                        injectorPid, injectorUid);
+                
+            case InputQueue.INPUT_EVENT_NATURE_TOUCH:
+                return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
+                        injectorPid, injectorUid);
+                
+            default:
+                return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+    }
+    
+    /* Gets the input targets for a trackball event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets,
+            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
+        WindowState focus = getFocusedWindow();
+        
+        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
+            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        }
+        
+        if (focus == null) {
+            return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+        
+        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+        
+        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
+        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+    }
+    
+    /* Set to true when a fat touch has been detected during the processing of a touch event.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Set to true whenever a fat touch is detected and reset to false on ACTION_UP.
+     */
+    private boolean mFatTouch;
+    
+    /* Set to true when we think the touch event.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Set to true on ACTION_DOWN and set to false on ACTION_UP.
+     */
+    private boolean mTouchDown;
+    
+    /* Current target of Motion events.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
+     */
+    private WindowState mTouchFocus;
 
-        // TODO detect cheek presses somewhere... either here or in native code
+    /* Windows above the target that would like to receive an "outside" touch event
+     * for any down events outside of them.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared immediately afterwards.
+     */
+    private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>();
+    
+    /* Wallpaper windows that are currently receiving touch events.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
+     */
+    private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>();
+    
+    /* Gets the input targets for a touch event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets,
+            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
+        final int action = event.getAction();
+        
+        if (action == MotionEvent.ACTION_DOWN) {
+            updateTouchFocusBeforeDownTd(event, policyFlags);
+        } else {
+            updateTouchFocusBeforeNonDownTd(event, policyFlags);
+        }
+
+        boolean skipDelivery = false;
+        int touchTargetFlags = 0;
+        
+        int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+        WindowState focusedTouchTarget = mTouchFocus;
+        if (focusedTouchTarget == null) {
+            // In this case we are either dropping the event, or have received
+            // a move or up without a down.  It is common to receive move
+            // events in such a way, since this means the user is moving the
+            // pointer without actually pressing down.  All other cases should
+            // be atypical, so let's log them.
+            if (action != MotionEvent.ACTION_MOVE) {
+                Slog.w(TAG, "No window to dispatch pointer action " + action);
+                injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED;
+            }
+        } else {
+            // We have a valid focused touch target.
+            if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) {
+                return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+            }
+            
+            wakeupIfNeeded(focusedTouchTarget, eventType(event));
+            
+            if ((focusedTouchTarget.mAttrs.flags &
+                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
+                // Target wants to ignore fat touch events
+                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
+                
+                if (cheekPress) {
+                    if ((action == MotionEvent.ACTION_DOWN)) {
+                        mFatTouch = true;
+                        skipDelivery = true;
+                    } else {
+                        if (! mFatTouch) {
+                            // cancel the earlier event
+                            touchTargetFlags |= InputTarget.FLAG_CANCEL;
+                            mFatTouch = true;
+                        } else {
+                            skipDelivery = true;
+                        }
+                    }
+                }
+            }
+        }
+        
+        if (! skipDelivery) {
+            int outsideTargetCount = mOutsideTouchTargets.size();
+            for (int i = 0; i < outsideTargetCount; i++) {
+                WindowState outsideTouchTarget = mOutsideTouchTargets.get(i);
+                addInputTargetTd(inputTargets, outsideTouchTarget,
+                        InputTarget.FLAG_OUTSIDE | touchTargetFlags);
+            }
+            
+            int wallpaperTargetCount = mWallpaperTouchTargets.size();
+            for (int i = 0; i < wallpaperTargetCount; i++) {
+                WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i);
+                addInputTargetTd(inputTargets, wallpaperTouchTarget,
+                        touchTargetFlags);
+            }
+            
+            if (focusedTouchTarget != null) {
+                addInputTargetTd(inputTargets, focusedTouchTarget,
+                        InputTarget.FLAG_SYNC | touchTargetFlags);
+            }
+        }
+
+        if (action == MotionEvent.ACTION_UP) {
+            updateTouchFocusAfterUpTd(event, policyFlags);
+        }
+        
+        return injectionResult;
+    }
+    
+    private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) {
+        if (mTouchDown) {
+            // This is weird, we got a down, but we thought it was already down!
+            // XXX: We should probably send an ACTION_UP to the current target.
+            Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus);
+            updateTouchFocusAfterUpTd(event, policyFlags);
+        }
+        
+        mTouchDown = true;
+        mPowerManager.logPointerDownEvent();
         
         final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-        
-        WindowState target = mTouchFocus;
-        
-        if (action == MotionEvent.ACTION_UP) {
-            // let go of our target
-            mPowerManager.logPointerUpEvent();
-            clearTouchFocus();
-        } else if (action == MotionEvent.ACTION_DOWN) {
-            // acquire a new target
-            mPowerManager.logPointerDownEvent();
-        
-            synchronized (mWindowMap) {
-                if (mTouchFocus != null) {
-                    // this is weird, we got a pen down, but we thought it was
-                    // already down!
-                    // XXX: We should probably send an ACTION_UP to the current
-                    // target.
-                    Slog.w(TAG, "Pointer down received while already down in: "
-                            + mTouchFocus);
-                    clearTouchFocus();
+        synchronized (mWindowMap) {
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+
+            final ArrayList windows = mWindows;
+            final int N = windows.size();
+            WindowState topErrWindow = null;
+            final Rect tmpRect = mTempRect;
+            for (int i= N - 1; i >= 0; i--) {
+                WindowState child = (WindowState) windows.get(i);
+                //Slog.i(TAG, "Checking dispatch to: " + child);
+                
+                final int flags = child.mAttrs.flags;
+                if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
+                    if (topErrWindow == null) {
+                        topErrWindow = child;
+                    }
                 }
-
-                // ACTION_DOWN is special, because we need to lock next events to
-                // the window we'll land onto.
-                final int x = (int) event.getX();
-                final int y = (int) event.getY();
-
-                final ArrayList windows = mWindows;
-                final int N = windows.size();
-                WindowState topErrWindow = null;
-                final Rect tmpRect = mTempRect;
-                for (int i=N-1; i>=0; i--) {
-                    WindowState child = (WindowState)windows.get(i);
-                    //Slog.i(TAG, "Checking dispatch to: " + child);
-                    final int flags = child.mAttrs.flags;
-                    if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
-                        if (topErrWindow == null) {
-                            topErrWindow = child;
-                        }
-                    }
-                    if (!child.isVisibleLw()) {
-                        //Slog.i(TAG, "Not visible!");
-                        continue;
-                    }
-                    if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
-                        //Slog.i(TAG, "Not touchable!");
-                        if ((flags & WindowManager.LayoutParams
-                                .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
-                            child.mNextOutsideTouch = mOutsideTouchTargets;
-                            mOutsideTouchTargets = child;
-                        }
-                        continue;
-                    }
+                
+                if (!child.isVisibleLw()) {
+                    //Slog.i(TAG, "Not visible!");
+                    continue;
+                }
+                
+                if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
                     tmpRect.set(child.mFrame);
                     if (child.mTouchableInsets == ViewTreeObserver
                                 .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
@@ -5197,7 +5429,7 @@
                         |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
                     if (tmpRect.contains(x, y) || touchFlags == 0) {
                         //Slog.i(TAG, "Using this target!");
-                        if (!screenWasOff || (flags &
+                        if (! screenWasOff || (flags &
                                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
                             mTouchFocus = child;
                         } else {
@@ -5206,143 +5438,76 @@
                         }
                         break;
                     }
-
-                    if ((flags & WindowManager.LayoutParams
-                            .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
-                        child.mNextOutsideTouch = mOutsideTouchTargets;
-                        mOutsideTouchTargets = child;
-                        //Slog.i(TAG, "Adding to outside target list: " + child);
-                    }
                 }
 
-                // if there's an error window but it's not accepting
-                // focus (typically because it is not yet visible) just
-                // wait for it -- any other focused window may in fact
-                // be in ANR state.
-                if (topErrWindow != null && mTouchFocus != topErrWindow) {
-                    mTouchFocus = null;
+                if ((flags & WindowManager.LayoutParams
+                        .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                    //Slog.i(TAG, "Adding to outside target list: " + child);
+                    mOutsideTouchTargets.add(child);
                 }
             }
-            
-            target = mTouchFocus;
-        }
-        
-        if (target != null) {
-            wakeupIfNeeded(target, eventType(event));
-        }
-        
-        int targetFlags = 0;
-        if (target == null) {
-            // In this case we are either dropping the event, or have received
-            // a move or up without a down.  It is common to receive move
-            // events in such a way, since this means the user is moving the
-            // pointer without actually pressing down.  All other cases should
-            // be atypical, so let's log them.
-            if (action != MotionEvent.ACTION_MOVE) {
-                Slog.w(TAG, "No window to dispatch pointer action " + action);
-            }
-        } else {
-            if ((target.mAttrs.flags &
-                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
-                //target wants to ignore fat touch events
-                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
-                //explicit flag to return without processing event further
-                boolean returnFlag = false;
-                if((action == MotionEvent.ACTION_DOWN)) {
-                    mFatTouch = false;
-                    if(cheekPress) {
-                        mFatTouch = true;
-                        returnFlag = true;
-                    }
-                } else {
-                    if(action == MotionEvent.ACTION_UP) {
-                        if(mFatTouch) {
-                            //earlier even was invalid doesnt matter if current up is cheekpress or not
-                            mFatTouch = false;
-                            returnFlag = true;
-                        } else if(cheekPress) {
-                            //cancel the earlier event
-                            targetFlags |= InputTarget.FLAG_CANCEL;
-                            action = MotionEvent.ACTION_CANCEL;
-                        }
-                    } else if(action == MotionEvent.ACTION_MOVE) {
-                        if(mFatTouch) {
-                            //two cases here
-                            //an invalid down followed by 0 or moves(valid or invalid)
-                            //a valid down,  invalid move, more moves. want to ignore till up
-                            returnFlag = true;
-                        } else if(cheekPress) {
-                            //valid down followed by invalid moves
-                            //an invalid move have to cancel earlier action
-                            targetFlags |= InputTarget.FLAG_CANCEL;
-                            action = MotionEvent.ACTION_CANCEL;
-                            if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
-                            //note that the subsequent invalid moves will not get here
-                            mFatTouch = true;
-                        }
-                    }
-                } //else if action
-                if(returnFlag) {
-                    return;
-                }
-            } //end if target
-        }        
-        
-        synchronized (mWindowMap) {
-            if (target != null && ! target.isVisibleLw()) {
-                target = null;
+
+            // If there's an error window but it's not accepting focus (typically because
+            // it is not yet visible) just wait for it -- any other focused window may in fact
+            // be in ANR state.
+            if (topErrWindow != null && mTouchFocus != topErrWindow) {
+                mTouchFocus = null;
             }
             
-            if (action == MotionEvent.ACTION_DOWN) {
-                while (mOutsideTouchTargets != null) {
-                    addInputTarget(inputTargets, mOutsideTouchTargets,
-                            InputTarget.FLAG_OUTSIDE | targetFlags);
-                    mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch;
-                }
+            // Drop the touch focus if the window is not visible.
+            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
+                mTouchFocus = null;
             }
             
-            // If we sent an initial down to the wallpaper, then continue
-            // sending events until the final up.
-            // Alternately if we are on top of the wallpaper, then the wallpaper also
-            // gets to see this movement.
-            if (mSendingPointersToWallpaper ||
-                    (target != null && action == MotionEvent.ACTION_DOWN
-                            && mWallpaperTarget == target
-                            && target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) {
+            // Determine wallpaper targets.
+            if (mTouchFocus != null
+                    && mTouchFocus == mWallpaperTarget
+                    && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
                 int curTokenIndex = mWallpaperTokens.size();
                 while (curTokenIndex > 0) {
                     curTokenIndex--;
                     WindowToken token = mWallpaperTokens.get(curTokenIndex);
+                    
                     int curWallpaperIndex = token.windows.size();
                     while (curWallpaperIndex > 0) {
                         curWallpaperIndex--;
                         WindowState wallpaper = token.windows.get(curWallpaperIndex);
                         if ((wallpaper.mAttrs.flags &
-                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
-                            continue;
+                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
+                            mWallpaperTouchTargets.add(wallpaper);
                         }
-                        
-                        switch (action) {
-                            case MotionEvent.ACTION_DOWN:
-                                mSendingPointersToWallpaper = true;
-                                break;
-                            case MotionEvent.ACTION_UP:
-                                mSendingPointersToWallpaper = false;
-                                break;
-                        }
-                        
-                        addInputTarget(inputTargets, wallpaper, targetFlags);
                     }
                 }
             }
-            
-            if (target != null) {
-                addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags);
+        }
+    }
+
+    private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) {
+        synchronized (mWindowMap) {
+            // Drop the touch focus if the window is not visible.
+            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
+                mTouchFocus = null;
+                mWallpaperTouchTargets.clear();
             }
         }
     }
     
-    private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) {
+    private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) {
+        mFatTouch = false;
+        mTouchDown = false;
+        mTouchFocus = null;
+        mOutsideTouchTargets.clear();
+        mWallpaperTouchTargets.clear();
+        
+        mPowerManager.logPointerUpEvent();
+    }
+
+    /* Adds a window to a list of input targets.
+     * Do NOT call this method while holding any locks because the call to
+     * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager
+     * and create a deadlock hazard.
+     */
+    private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) {
         if (window.mInputChannel == null) {
             return;
         }
@@ -5874,8 +6039,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectKeyEvent(newEvent,
-                    InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid);
+            result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchKey(newEvent, pid, uid);
             if (sync) {
@@ -5884,14 +6049,7 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
-        }
-        return false;
+        return reportInjectionResult(result);
     }
 
     /**
@@ -5910,8 +6068,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectMotionEvent(ev,
-                    InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid);
+            result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchPointer(null, ev, pid, uid);
             if (sync) {
@@ -5920,14 +6078,7 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
-        }
-        return false;
+        return reportInjectionResult(result);
     }
 
     /**
@@ -5946,8 +6097,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectMotionEvent(ev,
-                    InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid);
+            result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchTrackball(null, ev, pid, uid);
             if (sync) {
@@ -5956,14 +6107,37 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
+        return reportInjectionResult(result);
+    }
+    
+    private boolean reportInjectionResult(int result) {
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            switch (result) {
+                case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+                    Slog.w(TAG, "Input event injection permission denied.");
+                    throw new SecurityException(
+                            "Injecting to another application requires INJECT_EVENTS permission");
+                case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
+                    Slog.v(TAG, "Input event injection succeeded.");
+                    return true;
+                case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
+                    Slog.w(TAG, "Input event injection timed out.");
+                    return false;
+                case InputManager.INPUT_EVENT_INJECTION_FAILED:
+                default:
+                    Slog.w(TAG, "Input event injection failed.");
+                    return false;
+            }
+        } else {
+            switch (result) {
+                case INJECT_NO_PERMISSION:
+                    throw new SecurityException(
+                            "Injecting to another application requires INJECT_EVENTS permission");
+                case INJECT_SUCCEEDED:
+                    return true;
+            }
+            return false;
         }
-        return false;
     }
 
     private WindowState getFocusedWindow() {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ab3922f..1a6119a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -50,6 +50,9 @@
     jmethodID isScreenBright;
     jmethodID notifyConfigurationChanged;
     jmethodID notifyLidSwitchChanged;
+    jmethodID notifyInputChannelBroken;
+    jmethodID notifyInputChannelANR;
+    jmethodID notifyInputChannelRecoveredFromANR;
     jmethodID virtualKeyFeedback;
     jmethodID hackInterceptKey;
     jmethodID goToSleep;
@@ -73,6 +76,13 @@
     jfieldID height;
 } gVirtualKeyDefinitionClassInfo;
 
+static struct {
+    jclass clazz;
+
+    jmethodID ctor;
+    jfieldID mArray;
+} gInputTargetListClassInfo;
+
 // ----------------------------------------------------------------------------
 
 class NativeInputManager : public virtual RefBase,
@@ -89,6 +99,10 @@
     void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
     void setDisplayOrientation(int32_t displayId, int32_t orientation);
 
+    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
+            jweak inputChannelObjWeak);
+    status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
     /* --- InputReaderPolicyInterface implementation --- */
 
     virtual bool getDisplayInfo(int32_t displayId,
@@ -112,18 +126,20 @@
 
     virtual void notifyConfigurationChanged(nsecs_t when);
     virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
-    virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel);
+    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+            nsecs_t& outNewTimeout);
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
     virtual nsecs_t getKeyRepeatTimeout();
-    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets);
-    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets);
+    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
 
 private:
     sp<InputManager> mInputManager;
 
     jobject mCallbacksObj;
+    jobject mReusableInputTargetListObj;
 
     // Cached filtering policies.
     int32_t mFilterTouchEvents;
@@ -138,12 +154,20 @@
     bool isScreenOn();
     bool isScreenBright();
 
+    // Weak references to all currently registered input channels by receive fd.
+    Mutex mInputChannelRegistryLock;
+    KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
+
+    jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
     static inline JNIEnv* jniEnv() {
         return AndroidRuntime::getJNIEnv();
     }
 
     static bool isAppSwitchKey(int32_t keyCode);
-    static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName);
+    static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+    static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+            Vector<InputTarget>& outTargets);
 };
 
 // ----------------------------------------------------------------------------
@@ -155,6 +179,11 @@
 
     mCallbacksObj = env->NewGlobalRef(callbacksObj);
 
+    jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz,
+            gInputTargetListClassInfo.ctor);
+    mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj);
+    env->DeleteLocalRef(inputTargetListObj);
+
     sp<EventHub> eventHub = new EventHub();
     mInputManager = new InputManager(eventHub, this, this);
 }
@@ -163,13 +192,14 @@
     JNIEnv* env = jniEnv();
 
     env->DeleteGlobalRef(mCallbacksObj);
+    env->DeleteGlobalRef(mReusableInputTargetListObj);
 }
 
 bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
     return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
 }
 
-bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) {
+bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
     if (env->ExceptionCheck()) {
         LOGE("An exception was thrown by callback '%s'.", methodName);
         LOGE_EX(env);
@@ -196,6 +226,86 @@
     }
 }
 
+status_t NativeInputManager::registerInputChannel(JNIEnv* env,
+        const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+    jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
+    if (! inputChannelObjWeak) {
+        LOGE("Could not create weak reference for input channel.");
+        LOGE_EX(env);
+        return NO_MEMORY;
+    }
+
+    status_t status;
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index >= 0) {
+            LOGE("Input channel object '%s' has already been registered",
+                    inputChannel->getName().string());
+            status = INVALID_OPERATION;
+            goto DeleteWeakRef;
+        }
+
+        mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
+                inputChannelObjWeak);
+    }
+
+    status = mInputManager->registerInputChannel(inputChannel);
+    if (! status) {
+        return OK;
+    }
+
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+        mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
+    }
+
+DeleteWeakRef:
+    env->DeleteWeakGlobalRef(inputChannelObjWeak);
+    return status;
+}
+
+status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
+        const sp<InputChannel>& inputChannel) {
+    jweak inputChannelObjWeak;
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index < 0) {
+            LOGE("Input channel object '%s' is not currently registered",
+                    inputChannel->getName().string());
+            return INVALID_OPERATION;
+        }
+
+        inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+        mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
+    }
+
+    env->DeleteWeakGlobalRef(inputChannelObjWeak);
+
+    return mInputManager->unregisterInputChannel(inputChannel);
+}
+
+jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
+        const sp<InputChannel>& inputChannel) {
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index < 0) {
+            return NULL;
+        }
+
+        jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+        return env->NewLocalRef(inputChannelObjWeak);
+    }
+}
+
 bool NativeInputManager::getDisplayInfo(int32_t displayId,
         int32_t* width, int32_t* height, int32_t* orientation) {
     bool result = false;
@@ -216,7 +326,7 @@
     JNIEnv* env = jniEnv();
 
     jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
-    if (checkExceptionFromCallback(env, "isScreenOn")) {
+    if (checkAndClearExceptionFromCallback(env, "isScreenOn")) {
         return true;
     }
     return result;
@@ -226,7 +336,7 @@
     JNIEnv* env = jniEnv();
 
     jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
-    if (checkExceptionFromCallback(env, "isScreenBright")) {
+    if (checkAndClearExceptionFromCallback(env, "isScreenBright")) {
         return true;
     }
     return result;
@@ -245,7 +355,7 @@
 
     env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
             when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
-    checkExceptionFromCallback(env, "virtualKeyFeedback");
+    checkAndClearExceptionFromCallback(env, "virtualKeyFeedback");
 }
 
 int32_t NativeInputManager::interceptKey(nsecs_t when,
@@ -267,7 +377,7 @@
 
     jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
             deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
-    if (checkExceptionFromCallback(env, "hackInterceptKey")) {
+    if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) {
         wmActions = 0;
     }
 
@@ -284,12 +394,12 @@
 
     if (wmActions & WM_ACTION_GO_TO_SLEEP) {
         env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
-        checkExceptionFromCallback(env, "goToSleep");
+        checkAndClearExceptionFromCallback(env, "goToSleep");
     }
 
     if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
         env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
-        checkExceptionFromCallback(env, "pokeUserActivityForKey");
+        checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
     }
 
     if (wmActions & WM_ACTION_PASS_TO_USER) {
@@ -297,7 +407,7 @@
 
         if (down && isAppSwitchKey(keyCode)) {
             env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
-            checkExceptionFromCallback(env, "notifyAppSwitchComing");
+            checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
 
             actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
         }
@@ -358,7 +468,7 @@
     case SW_LID:
         env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
                 when, switchValue == 0);
-        checkExceptionFromCallback(env, "notifyLidSwitchChanged");
+        checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
         break;
     }
 
@@ -371,7 +481,7 @@
 
         jboolean result = env->CallBooleanMethod(mCallbacksObj,
                 gCallbacksClassInfo.filterTouchEvents);
-        if (checkExceptionFromCallback(env, "filterTouchEvents")) {
+        if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
             result = false;
         }
 
@@ -386,7 +496,7 @@
 
         jboolean result = env->CallBooleanMethod(mCallbacksObj,
                 gCallbacksClassInfo.filterJumpyTouchEvents);
-        if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) {
+        if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
             result = false;
         }
 
@@ -400,10 +510,10 @@
     JNIEnv* env = jniEnv();
 
     jstring deviceNameStr = env->NewStringUTF(deviceName.string());
-    if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
+    if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
         jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
                 gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
-        if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
+        if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
             jsize length = env->GetArrayLength(result);
             for (jsize i = 0; i < length; i++) {
                 jobject item = env->GetObjectArrayElement(result, i);
@@ -433,7 +543,7 @@
 
     jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
             gCallbacksClassInfo.getExcludedDeviceNames));
-    if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
+    if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
         jsize length = env->GetArrayLength(result);
         for (jsize i = 0; i < length; i++) {
             jstring item = jstring(env->GetObjectArrayElement(result, i));
@@ -460,7 +570,7 @@
 
     env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
             when, config.touchScreen, config.keyboard, config.navigation);
-    checkExceptionFromCallback(env, "notifyConfigurationChanged");
+    checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
 }
 
 void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
@@ -468,16 +578,47 @@
     LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
 #endif
 
-    // TODO
+    JNIEnv* env = jniEnv();
+
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken,
+                inputChannelObjLocal);
+        checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    }
 }
 
-void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
+bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+        nsecs_t& outNewTimeout) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     LOGD("notifyInputChannelANR - inputChannel='%s'",
             inputChannel->getName().string());
 #endif
 
-    // TODO
+    JNIEnv* env = jniEnv();
+
+    jlong newTimeout;
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        newTimeout = env->CallLongMethod(mCallbacksObj,
+                gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
+        if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
+            newTimeout = -2;
+        }
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    } else {
+        newTimeout = -2;
+    }
+
+    if (newTimeout == -2) {
+        return false; // abort
+    }
+
+    outNewTimeout = newTimeout;
+    return true; // resume
 }
 
 void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
@@ -486,7 +627,16 @@
             inputChannel->getName().string());
 #endif
 
-    // TODO
+    JNIEnv* env = jniEnv();
+
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
+                inputChannelObjLocal);
+        checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    }
 }
 
 nsecs_t NativeInputManager::getKeyRepeatTimeout() {
@@ -499,73 +649,83 @@
     }
 }
 
-void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-        Vector<InputTarget>& outTargets) {
+int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("getKeyEventTargets - policyFlags=%d", policyFlags);
+    LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
 #endif
 
     JNIEnv* env = jniEnv();
 
+    jint injectionResult;
     jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
     if (! keyEventObj) {
         LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
     } else {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
-                gCallbacksClassInfo.getKeyEventTargets,
-                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
-        if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-                if (! item) {
-                    break; // found null element indicating end of used portion of the array
-                }
-
-                outTargets.add();
-                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
+        jint injectionResult = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
+                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
+                jint(injectorPid), jint(injectorUid));
+        if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
         }
         env->DeleteLocalRef(keyEventObj);
     }
+    return injectionResult;
 }
 
-void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-        Vector<InputTarget>& outTargets) {
+int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("getMotionEventTargets - policyFlags=%d", policyFlags);
+    LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
 #endif
 
     JNIEnv* env = jniEnv();
 
+    jint injectionResult;
     jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
     if (! motionEventObj) {
         LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
     } else {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
-                gCallbacksClassInfo.getMotionEventTargets,
-                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
-        if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-                if (! item) {
-                    break; // found null element indicating end of used portion of the array
-                }
-
-                outTargets.add();
-                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
+        jint injectionResult = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj,
+                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags),
+                jint(injectorPid), jint(injectorUid));
+        if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) {
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
         }
         android_view_MotionEvent_recycle(env, motionEventObj);
         env->DeleteLocalRef(motionEventObj);
     }
+    return injectionResult;
+}
+
+void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+        Vector<InputTarget>& outTargets) {
+    jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
+            inputTargetListObj, gInputTargetListClassInfo.mArray));
+
+    jsize length = env->GetArrayLength(inputTargetArray);
+    for (jsize i = 0; i < length; i++) {
+        jobject item = env->GetObjectArrayElement(inputTargetArray, i);
+        if (! item) {
+            break; // found null element indicating end of used portion of the array
+        }
+
+        outTargets.add();
+        android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+
+        env->DeleteLocalRef(item);
+    }
+    env->DeleteLocalRef(inputTargetArray);
 }
 
 
@@ -686,7 +846,7 @@
             "the input manager!", inputChannel->getName().string());
 
     if (gNativeInputManager != NULL) {
-        gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
+        gNativeInputManager->unregisterInputChannel(env, inputChannel);
     }
 }
 
@@ -703,7 +863,9 @@
         return;
     }
 
-    status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel);
+
+    status_t status = gNativeInputManager->registerInputChannel(
+            env, inputChannel, inputChannelObj);
     if (status) {
         jniThrowRuntimeException(env, "Failed to register input channel.  "
                 "Check logs for details.");
@@ -729,13 +891,41 @@
 
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
 
-    status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
+    status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
     if (status) {
         jniThrowRuntimeException(env, "Failed to unregister input channel.  "
                 "Check logs for details.");
     }
 }
 
+static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz,
+        jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jboolean sync, jint timeoutMillis) {
+    if (checkInputManagerUnitialized(env)) {
+        return INPUT_EVENT_INJECTION_FAILED;
+    }
+
+    KeyEvent keyEvent;
+    android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent);
+
+    return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
+            injectorPid, injectorUid, sync, timeoutMillis);
+}
+
+static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz,
+        jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jboolean sync, jint timeoutMillis) {
+    if (checkInputManagerUnitialized(env)) {
+        return INPUT_EVENT_INJECTION_FAILED;
+    }
+
+    MotionEvent motionEvent;
+    android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent);
+
+    return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
+            injectorPid, injectorUid, sync, timeoutMillis);
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gInputManagerMethods[] = {
@@ -759,7 +949,11 @@
     { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
-            (void*) android_server_InputManager_nativeUnregisterInputChannel }
+            (void*) android_server_InputManager_nativeUnregisterInputChannel },
+    { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
+            (void*) android_server_InputManager_nativeInjectKeyEvent },
+    { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
+            (void*) android_server_InputManager_nativeInjectMotionEvent }
 };
 
 #define FIND_CLASS(var, className) \
@@ -796,6 +990,15 @@
     GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
             "notifyLidSwitchChanged", "(JZ)V");
 
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
+            "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
+
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
+            "notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
+
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
+            "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
+
     GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
             "virtualKeyFeedback", "(JIIIIIIJ)V");
 
@@ -825,10 +1028,12 @@
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
     GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
-            "getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;");
+            "getKeyEventTargets",
+            "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
 
     GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
-            "getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;");
+            "getMotionEventTargets",
+            "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
 
     // VirtualKeyDefinition
 
@@ -850,6 +1055,16 @@
     GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
             "height", "I");
 
+    // InputTargetList
+
+    FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+
+    GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
+            "<init>", "()V");
+
+    GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
+            "mArray", "[Landroid/view/InputTarget;");
+
     return 0;
 }