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;
}