summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/NativeActivity.java6
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/view/InputHandler.java4
-rw-r--r--core/java/android/view/InputQueue.java16
-rw-r--r--core/java/android/view/ViewRoot.java433
-rw-r--r--core/java/android/view/WindowManagerPolicy.java29
-rw-r--r--core/java/com/android/internal/view/BaseInputHandler.java9
-rw-r--r--core/jni/android_app_NativeActivity.cpp22
-rw-r--r--core/jni/android_view_InputQueue.cpp21
-rw-r--r--include/android_runtime/android_app_NativeActivity.h2
-rw-r--r--include/ui/InputDispatcher.h12
-rw-r--r--include/ui/InputTransport.h7
-rw-r--r--libs/ui/InputDispatcher.cpp59
-rw-r--r--libs/ui/InputTransport.cpp27
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp5
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp14
-rw-r--r--native/android/input.cpp2
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java31
-rw-r--r--services/java/com/android/server/InputManager.java8
-rw-r--r--services/java/com/android/server/WindowManagerService.java28
-rw-r--r--services/jni/com_android_server_InputManager.cpp29
21 files changed, 486 insertions, 285 deletions
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index de36f27..a5c49ec 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -344,12 +344,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
}
- void dispatchUnhandledKeyEvent(KeyEvent event) {
+ boolean dispatchUnhandledKeyEvent(KeyEvent event) {
try {
mDispatchingUnhandledKey = true;
View decor = getWindow().getDecorView();
if (decor != null) {
- decor.dispatchKeyEvent(event);
+ return decor.dispatchKeyEvent(event);
+ } else {
+ return false;
}
} finally {
mDispatchingUnhandledKey = false;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26346d2..755e39f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -219,14 +219,17 @@ public abstract class WallpaperService extends Service {
final InputHandler mInputHandler = new BaseInputHandler() {
@Override
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ public void handleMotion(MotionEvent event,
+ InputQueue.FinishedCallback finishedCallback) {
+ boolean handled = false;
try {
int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
dispatchPointer(event);
+ handled = true;
}
} finally {
- finishedCallback.run();
+ finishedCallback.finished(handled);
}
}
};
diff --git a/core/java/android/view/InputHandler.java b/core/java/android/view/InputHandler.java
index 41a152d..14ce14c 100644
--- a/core/java/android/view/InputHandler.java
+++ b/core/java/android/view/InputHandler.java
@@ -29,7 +29,7 @@ public interface InputHandler {
* @param event The key event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
- public void handleKey(KeyEvent event, Runnable finishedCallback);
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback);
/**
* Handle a motion event.
@@ -39,5 +39,5 @@ public interface InputHandler {
* @param event The motion event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
- public void handleMotion(MotionEvent event, Runnable finishedCallback);
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback);
}
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 9e800df..5735b63 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -53,7 +53,7 @@ public final class InputQueue {
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputHandler inputHandler, MessageQueue messageQueue);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
- private static native void nativeFinished(long finishedToken);
+ private static native void nativeFinished(long finishedToken, boolean handled);
/** @hide */
public InputQueue(InputChannel channel) {
@@ -116,18 +116,22 @@ public final class InputQueue {
@SuppressWarnings("unused")
private static void dispatchKeyEvent(InputHandler inputHandler,
KeyEvent event, long finishedToken) {
- Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
+ FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleKey(event, finishedCallback);
}
@SuppressWarnings("unused")
private static void dispatchMotionEvent(InputHandler inputHandler,
MotionEvent event, long finishedToken) {
- Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
+ FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleMotion(event, finishedCallback);
}
- private static class FinishedCallback implements Runnable {
+ /**
+ * A callback that must be invoked to when finished processing an event.
+ * @hide
+ */
+ public static final class FinishedCallback {
private static final boolean DEBUG_RECYCLING = false;
private static final int RECYCLE_MAX_COUNT = 4;
@@ -156,13 +160,13 @@ public final class InputQueue {
}
}
- public void run() {
+ public void finished(boolean handled) {
synchronized (sLock) {
if (mFinishedToken == -1) {
throw new IllegalStateException("Event finished callback already invoked.");
}
- nativeFinished(mFinishedToken);
+ nativeFinished(mFinishedToken, handled);
mFinishedToken = -1;
if (sRecycleCount < RECYCLE_MAX_COUNT) {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5b18715..fa7fe80 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -53,6 +53,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputQueue.FinishedCallback;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -1742,34 +1743,14 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
handleFinishedEvent(msg.arg1, msg.arg2 != 0);
break;
case DISPATCH_KEY:
- if (LOCAL_LOGV) Log.v(
- TAG, "Dispatching key "
- + msg.obj + " to " + mView);
deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
break;
- case DISPATCH_POINTER: {
- MotionEvent event = (MotionEvent) msg.obj;
- try {
- deliverPointerEvent(event);
- } finally {
- event.recycle();
- if (msg.arg1 != 0) {
- finishInputEvent();
- }
- if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
- }
- } break;
- case DISPATCH_TRACKBALL: {
- MotionEvent event = (MotionEvent) msg.obj;
- try {
- deliverTrackballEvent(event);
- } finally {
- event.recycle();
- if (msg.arg1 != 0) {
- finishInputEvent();
- }
- }
- } break;
+ case DISPATCH_POINTER:
+ deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ break;
+ case DISPATCH_TRACKBALL:
+ deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
@@ -1871,7 +1852,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
// system! Bad bad bad!
event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
- deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
+ deliverKeyEventPostIme((KeyEvent)msg.obj, false);
} break;
case FINISH_INPUT_CONNECTION: {
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1897,7 +1878,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
}
- private void startInputEvent(Runnable finishedCallback) {
+ private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
+ "already an unfinished input event in progress.");
@@ -1906,11 +1887,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mFinishedCallback = finishedCallback;
}
- private void finishInputEvent() {
+ private void finishInputEvent(boolean handled) {
if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
if (mFinishedCallback != null) {
- mFinishedCallback.run();
+ mFinishedCallback.finished(handled);
mFinishedCallback = null;
} else {
Slog.w(TAG, "Attempted to tell the input queue that the current input event "
@@ -2039,105 +2020,134 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
return false;
}
- private void deliverPointerEvent(MotionEvent event) {
+ private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishPointerEvent(event, sendDone, false);
+ return;
+ }
+
+ // Translate the pointer event for compatibility, if needed.
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
}
-
- boolean handled;
- if (mView != null && mAdded) {
- // enter touch mode on the down
- boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
- if (isDown) {
- ensureTouchMode(true);
- }
- if(Config.LOGV) {
- captureMotionLog("captureDispatchPointer", event);
- }
- if (mCurScrollY != 0) {
- event.offsetLocation(0, mCurScrollY);
- }
- if (MEASURE_LATENCY) {
- lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
- }
- // cache for possible drag-initiation
- mLastTouchPoint.x = event.getRawX();
- mLastTouchPoint.y = event.getRawY();
- handled = mView.dispatchTouchEvent(event);
- if (MEASURE_LATENCY) {
- lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
- }
- if (!handled && isDown) {
- int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
-
- final int edgeFlags = event.getEdgeFlags();
- int direction = View.FOCUS_UP;
- int x = (int)event.getX();
- int y = (int)event.getY();
- final int[] deltas = new int[2];
-
- if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
- direction = View.FOCUS_DOWN;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
- direction = View.FOCUS_UP;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- direction = View.FOCUS_RIGHT;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- direction = View.FOCUS_LEFT;
- }
+ // Enter touch mode on the down.
+ boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
+ if (isDown) {
+ ensureTouchMode(true);
+ }
+ if(Config.LOGV) {
+ captureMotionLog("captureDispatchPointer", event);
+ }
- if (edgeFlags != 0 && mView instanceof ViewGroup) {
- View nearest = FocusFinder.getInstance().findNearestTouchable(
- ((ViewGroup) mView), x, y, direction, deltas);
- if (nearest != null) {
- event.offsetLocation(deltas[0], deltas[1]);
- event.setEdgeFlags(0);
- mView.dispatchTouchEvent(event);
- }
+ // Offset the scroll position.
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+ if (MEASURE_LATENCY) {
+ lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+ }
+
+ // Remember the touch position for possible drag-initiation.
+ mLastTouchPoint.x = event.getRawX();
+ mLastTouchPoint.y = event.getRawY();
+
+ // Dispatch touch to view hierarchy.
+ boolean handled = mView.dispatchTouchEvent(event);
+ if (MEASURE_LATENCY) {
+ lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+ }
+ if (handled) {
+ finishPointerEvent(event, sendDone, true);
+ return;
+ }
+
+ // Apply edge slop and try again, if appropriate.
+ final int edgeFlags = event.getEdgeFlags();
+ if (edgeFlags != 0 && mView instanceof ViewGroup) {
+ final int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
+ int direction = View.FOCUS_UP;
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+ final int[] deltas = new int[2];
+
+ if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
+ direction = View.FOCUS_DOWN;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
+ direction = View.FOCUS_UP;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ direction = View.FOCUS_RIGHT;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ direction = View.FOCUS_LEFT;
+ }
+
+ View nearest = FocusFinder.getInstance().findNearestTouchable(
+ ((ViewGroup) mView), x, y, direction, deltas);
+ if (nearest != null) {
+ event.offsetLocation(deltas[0], deltas[1]);
+ event.setEdgeFlags(0);
+ if (mView.dispatchTouchEvent(event)) {
+ finishPointerEvent(event, sendDone, true);
+ return;
}
}
}
+
+ // Pointer event was unhandled.
+ finishPointerEvent(event, sendDone, false);
}
- private void deliverTrackballEvent(MotionEvent event) {
+ private void finishPointerEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ event.recycle();
+ if (sendDone) {
+ finishInputEvent(handled);
+ }
+ if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
+ }
+
+ private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
- boolean handled = false;
- if (mView != null && mAdded) {
- handled = mView.dispatchTrackballEvent(event);
- if (handled) {
- // If we reach this, we delivered a trackball event to mView and
- // mView consumed it. Because we will not translate the trackball
- // event into a key event, touch mode will not exit, so we exit
- // touch mode here.
- ensureTouchMode(false);
- return;
- }
-
- // Otherwise we could do something here, like changing the focus
- // or something?
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishTrackballEvent(event, sendDone, false);
+ return;
+ }
+
+ // Deliver the trackball event to the view.
+ if (mView.dispatchTrackballEvent(event)) {
+ // If we reach this, we delivered a trackball event to mView and
+ // mView consumed it. Because we will not translate the trackball
+ // event into a key event, touch mode will not exit, so we exit
+ // touch mode here.
+ ensureTouchMode(false);
+
+ finishTrackballEvent(event, sendDone, true);
+ mLastTrackballTime = Integer.MIN_VALUE;
+ return;
}
+ // Translate the trackball event into DPAD keys and try to deliver those.
final TrackballAxis x = mTrackballAxisX;
final TrackballAxis y = mTrackballAxisY;
long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime+MAX_TRACKBALL_DELAY) < curTime) {
+ if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
// It has been too long since the last movement,
// so restart at the beginning.
x.reset(0);
@@ -2226,6 +2236,17 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
mLastTrackballTime = curTime;
}
+
+ // Unfortunately we can't tell whether the application consumed the keys, so
+ // we always consider the trackball event handled.
+ finishTrackballEvent(event, sendDone, true);
+ }
+
+ private void finishTrackballEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ event.recycle();
+ if (sendDone) {
+ finishInputEvent(handled);
+ }
}
/**
@@ -2371,123 +2392,137 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- // If mView is null, we just consume the key event because it doesn't
- // make sense to do anything else with it.
- boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
- if (handled) {
- if (sendDone) {
- finishInputEvent();
- }
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishKeyEvent(event, sendDone, false);
+ return;
+ }
+
+ if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
+
+ // Perform predispatching before the IME.
+ if (mView.dispatchKeyEventPreIme(event)) {
+ finishKeyEvent(event, sendDone, true);
return;
}
- // If it is possible for this window to interact with the input
- // method window, then we want to first dispatch our key events
- // to the input method.
+
+ // Dispatch to the IME before propagating down the view hierarchy.
+ // The IME will eventually call back into handleFinishedEvent.
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mView != null) {
+ if (imm != null) {
int seq = enqueuePendingEvent(event, sendDone);
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ seq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), seq, event,
- mInputMethodCallback);
+ imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
return;
}
}
- deliverKeyEventToViewHierarchy(event, sendDone);
+
+ // Not dispatching to IME, continue with post IME actions.
+ deliverKeyEventPostIme(event, sendDone);
}
- void handleFinishedEvent(int seq, boolean handled) {
+ private void handleFinishedEvent(int seq, boolean handled) {
final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
+ " handled=" + handled + " event=" + event);
if (event != null) {
final boolean sendDone = seq >= 0;
- if (!handled) {
- deliverKeyEventToViewHierarchy(event, sendDone);
- } else if (sendDone) {
- finishInputEvent();
+ if (handled) {
+ finishKeyEvent(event, sendDone, true);
} else {
- Log.w(TAG, "handleFinishedEvent(seq=" + seq
- + " handled=" + handled + " ev=" + event
- + ") neither delivering nor finishing key");
+ deliverKeyEventPostIme(event, sendDone);
}
}
}
- private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
- try {
- if (mView != null && mAdded) {
- final int action = event.getAction();
- boolean isDown = (action == KeyEvent.ACTION_DOWN);
+ private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
+ // If the view went away, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishKeyEvent(event, sendDone, false);
+ return;
+ }
- if (checkForLeavingTouchModeAndConsume(event)) {
- return;
- }
+ // If the key's purpose is to exit touch mode then we consume it and consider it handled.
+ if (checkForLeavingTouchModeAndConsume(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (Config.LOGV) {
- captureKeyLog("captureDispatchKeyEvent", event);
- }
- mFallbackEventHandler.preDispatchKeyEvent(event);
- boolean keyHandled = mView.dispatchKeyEvent(event);
+ if (Config.LOGV) {
+ captureKeyLog("captureDispatchKeyEvent", event);
+ }
- if (!keyHandled) {
- mFallbackEventHandler.dispatchKeyEvent(event);
- }
+ // Deliver the key to the view hierarchy.
+ if (mView.dispatchKeyEvent(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (!keyHandled && isDown) {
- int direction = 0;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- direction = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- direction = View.FOCUS_RIGHT;
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- direction = View.FOCUS_UP;
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- direction = View.FOCUS_DOWN;
- break;
- }
+ // Apply the fallback event policy.
+ if (mFallbackEventHandler.dispatchKeyEvent(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (direction != 0) {
-
- View focused = mView != null ? mView.findFocus() : null;
- if (focused != null) {
- View v = focused.focusSearch(direction);
- boolean focusPassed = false;
- if (v != null && v != focused) {
- // do the math the get the interesting rect
- // of previous focused into the coord system of
- // newly focused view
- focused.getFocusedRect(mTempRect);
- if (mView instanceof ViewGroup) {
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(
- focused, mTempRect);
- ((ViewGroup) mView).offsetRectIntoDescendantCoords(
- v, mTempRect);
- }
- focusPassed = v.requestFocus(direction, mTempRect);
- }
+ // Handle automatic focus changes.
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ direction = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ direction = View.FOCUS_RIGHT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ direction = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ direction = View.FOCUS_DOWN;
+ break;
+ }
- if (!focusPassed) {
- mView.dispatchUnhandledMove(focused, direction);
- } else {
- playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
- }
+ if (direction != 0) {
+ View focused = mView != null ? mView.findFocus() : null;
+ if (focused != null) {
+ View v = focused.focusSearch(direction);
+ if (v != null && v != focused) {
+ // do the math the get the interesting rect
+ // of previous focused into the coord system of
+ // newly focused view
+ focused.getFocusedRect(mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focused, mTempRect);
+ ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+ v, mTempRect);
+ }
+ if (v.requestFocus(direction, mTempRect)) {
+ playSoundEffect(
+ SoundEffectConstants.getContantForFocusDirection(direction));
+ finishKeyEvent(event, sendDone, true);
+ return;
}
}
+
+ // Give the focused view a last chance to handle the dpad key.
+ if (mView.dispatchUnhandledMove(focused, direction)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
}
}
+ }
- } finally {
- if (sendDone) {
- finishInputEvent();
- }
- // Let the exception fall through -- the looper will catch
- // it and take care of the bad app for us.
+ // Key was unhandled.
+ finishKeyEvent(event, sendDone, false);
+ }
+
+ private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
+ if (sendDone) {
+ finishInputEvent(handled);
}
}
@@ -2759,15 +2794,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
sendMessage(msg);
}
- private Runnable mFinishedCallback;
+ private InputQueue.FinishedCallback mFinishedCallback;
private final InputHandler mInputHandler = new InputHandler() {
- public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
startInputEvent(finishedCallback);
dispatchKey(event, true);
}
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
startInputEvent(finishedCallback);
dispatchMotion(event, true);
}
@@ -2814,7 +2849,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
// TODO
Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
if (sendDone) {
- finishInputEvent();
+ finishInputEvent(false);
}
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index e78d6a8..4deff5e 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -568,12 +568,8 @@ public interface WindowManagerPolicy {
* Called from the input dispatcher thread before a key is dispatched to a window.
*
* <p>Allows you to define
- * behavior for keys that can not be overridden by applications or redirect
- * key events to a different window. This method is called from the
- * input thread, with no locks held.
- *
- * <p>Note that if you change the window a key is dispatched to, the new
- * target window will receive the key event without having input focus.
+ * behavior for keys that can not be overridden by applications.
+ * This method is called from the input thread, with no locks held.
*
* @param win The window that currently has focus. This is where the key
* event will normally go.
@@ -591,6 +587,27 @@ public interface WindowManagerPolicy {
int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
/**
+ * Called from the input dispatcher thread when an application did not handle
+ * a key that was dispatched to it.
+ *
+ * <p>Allows you to define default global behavior for keys that were not handled
+ * by applications. This method is called from the input thread, with no locks held.
+ *
+ * @param win The window that currently has focus. This is where the key
+ * event will normally go.
+ * @param action The key event action.
+ * @param flags The key event flags.
+ * @param keyCode The key code.
+ * @param scanCode The key's scan code.
+ * @param metaState bit mask of meta keys that are held.
+ * @param repeatCount Number of times a key down has repeated.
+ * @param policyFlags The policy flags associated with the key.
+ * @return Returns true if the policy consumed the event.
+ */
+ public boolean dispatchUnhandledKey(WindowState win, int action, int flags,
+ int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
+
+ /**
* Called when layout of the windows is about to start.
*
* @param displayWidth The current full width of the screen.
diff --git a/core/java/com/android/internal/view/BaseInputHandler.java b/core/java/com/android/internal/view/BaseInputHandler.java
index e943a7d..74b4b06 100644
--- a/core/java/com/android/internal/view/BaseInputHandler.java
+++ b/core/java/com/android/internal/view/BaseInputHandler.java
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.view.InputHandler;
+import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -25,11 +26,11 @@ import android.view.MotionEvent;
* @hide
*/
public abstract class BaseInputHandler implements InputHandler {
- public void handleKey(KeyEvent event, Runnable finishedCallback) {
- finishedCallback.run();
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
+ finishedCallback.finished(false);
}
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
- finishedCallback.run();
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ finishedCallback.finished(false);
}
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index a586f49..695d50a4 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -195,7 +195,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
mLock.unlock();
if (finishNow) {
- finishEvent(*outEvent, true);
+ finishEvent(*outEvent, true, false);
*outEvent = NULL;
return -1;
} else if (*outEvent != NULL) {
@@ -215,7 +215,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
if (res != android::OK) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
mConsumer.getChannel()->getName().string(), res);
- mConsumer.sendFinishedSignal();
+ mConsumer.sendFinishedSignal(false);
return -1;
}
@@ -245,10 +245,12 @@ bool AInputQueue::preDispatchEvent(AInputEvent* event) {
return preDispatchKey((KeyEvent*)event);
}
-void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
- LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
+void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) {
+ LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
+ handled ? 1 : 0, didDefaultHandling ? 1 : 0);
- if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
+ if (!handled && !didDefaultHandling
+ && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
&& ((KeyEvent*)event)->hasDefaultAction()) {
// The app didn't handle this, but it may have a default action
// associated with it. We need to hand this back to Java to be
@@ -263,7 +265,7 @@ void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
const in_flight_event& inflight(mInFlightEvents[i]);
if (inflight.event == event) {
if (inflight.doFinish) {
- int32_t res = mConsumer.sendFinishedSignal();
+ int32_t res = mConsumer.sendFinishedSignal(handled);
if (res != android::OK) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",
mConsumer.getChannel()->getName().string(), res);
@@ -577,10 +579,11 @@ static int mainWorkCallback(int fd, int events, void* data) {
while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
- code->env->CallVoidMethod(code->clazz,
+ jboolean handled = code->env->CallBooleanMethod(code->clazz,
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
- code->nativeInputQueue->finishEvent(keyEvent, true);
+ code->env->DeleteLocalRef(inputEventObj);
+ code->nativeInputQueue->finishEvent(keyEvent, handled, true);
}
int seq;
while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
@@ -589,6 +592,7 @@ static int mainWorkCallback(int fd, int events, void* data) {
code->env->CallVoidMethod(code->clazz,
gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
+ code->env->DeleteLocalRef(inputEventObj);
}
} break;
case CMD_FINISH: {
@@ -1044,7 +1048,7 @@ int register_android_app_NativeActivity(JNIEnv* env)
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
gNativeActivityClassInfo.clazz,
- "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
+ "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
gNativeActivityClassInfo.clazz,
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 282e9ed..b5a5d2e 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -61,7 +61,7 @@ public:
status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj);
- status_t finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish);
+ status_t finished(JNIEnv* env, jlong finishedToken, bool handled, bool ignoreSpuriousFinish);
private:
class Connection : public RefBase {
@@ -211,7 +211,7 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan
"while an input message is still in progress.",
connection->getInputChannelName());
connection->messageInProgress = false;
- connection->inputConsumer.sendFinishedSignal(); // ignoring result
+ connection->inputConsumer.sendFinishedSignal(false); // ignoring result
}
} // release lock
@@ -231,7 +231,8 @@ ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChanne
return -1;
}
-status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
+status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken,
+ bool handled, bool ignoreSpuriousFinish) {
int32_t receiveFd;
uint16_t connectionId;
uint16_t messageSeqNum;
@@ -268,7 +269,7 @@ status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignor
connection->messageInProgress = false;
- status_t status = connection->inputConsumer.sendFinishedSignal();
+ status_t status = connection->inputConsumer.sendFinishedSignal(handled);
if (status) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",
connection->getInputChannelName(), status);
@@ -341,7 +342,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
if (status) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
connection->getInputChannelName(), status);
- connection->inputConsumer.sendFinishedSignal();
+ connection->inputConsumer.sendFinishedSignal(false);
return 1;
}
@@ -393,7 +394,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
LOGW("channel '%s' ~ Failed to obtain DVM event object.",
connection->getInputChannelName());
env->DeleteLocalRef(inputHandlerObjLocal);
- q->finished(env, finishedToken, false);
+ q->finished(env, finishedToken, false, false);
return 1;
}
@@ -412,7 +413,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
LOGE_EX(env);
env->ExceptionClear();
- q->finished(env, finishedToken, true /*ignoreSpuriousFinish*/);
+ q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
}
env->DeleteLocalRef(inputEventObj);
@@ -470,9 +471,9 @@ static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jc
}
static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
- jlong finishedToken) {
+ jlong finishedToken, bool handled) {
status_t status = gNativeInputQueue.finished(
- env, finishedToken, false /*ignoreSpuriousFinish*/);
+ env, finishedToken, handled, false /*ignoreSpuriousFinish*/);
// We ignore the case where an event could not be finished because the input channel
// was no longer registered (DEAD_OBJECT) since it is a common race that can occur
@@ -493,7 +494,7 @@ static JNINativeMethod gInputQueueMethods[] = {
{ "nativeUnregisterInputChannel",
"(Landroid/view/InputChannel;)V",
(void*)android_view_InputQueue_nativeUnregisterInputChannel },
- { "nativeFinished", "(J)V",
+ { "nativeFinished", "(JZ)V",
(void*)android_view_InputQueue_nativeFinished }
};
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
index 5dbec59..990143b 100644
--- a/include/android_runtime/android_app_NativeActivity.h
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -83,7 +83,7 @@ public:
bool preDispatchEvent(AInputEvent* event);
- void finishEvent(AInputEvent* event, bool handled);
+ void finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling);
// ----------------------------------------------------------
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index a5591ba..d09ff41 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -304,6 +304,10 @@ public:
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
+ /* Allows the policy a chance to perform default processing for an unhandled key. */
+ virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
+
/* Notifies the policy about switch events.
*/
virtual void notifySwitch(nsecs_t when,
@@ -609,6 +613,7 @@ private:
sp<InputChannel> inputChannel;
sp<InputApplicationHandle> inputApplicationHandle;
int32_t userActivityEventType;
+ bool handled;
};
// Generic queue implementation.
@@ -1030,7 +1035,8 @@ private:
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ bool handled);
void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void drainOutboundQueueLocked(Connection* connection);
@@ -1061,7 +1067,7 @@ private:
void onDispatchCycleStartedLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection);
+ nsecs_t currentTime, const sp<Connection>& connection, bool handled);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onANRLocked(
@@ -1073,7 +1079,9 @@ private:
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
+ void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+ void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
// Statistics gathering.
void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index dc9e27a..7efb6cc 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -250,12 +250,13 @@ public:
status_t sendDispatchSignal();
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
+ * Returns whether the consumer handled the message.
*
* Returns OK on success.
* Returns WOULD_BLOCK if there is no signal present.
* Other errors probably indicate that the channel is broken.
*/
- status_t receiveFinishedSignal();
+ status_t receiveFinishedSignal(bool& outHandled);
private:
sp<InputChannel> mChannel;
@@ -305,12 +306,12 @@ public:
status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
/* Sends a finished signal to the publisher to inform it that the current message is
- * finished processing.
+ * finished processing and specifies whether the message was handled by the consumer.
*
* Returns OK on success.
* Errors probably indicate that the channel is broken.
*/
- status_t sendFinishedSignal();
+ status_t sendFinishedSignal(bool handled);
/* Receives the dispatched signal from the publisher.
*
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index f9c0b91..299b1ba 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -1770,13 +1770,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
+ const sp<Connection>& connection, bool handled) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
- "%01.1fms since dispatch",
+ "%01.1fms since dispatch, handled=%s",
connection->getInputChannelName(),
connection->getEventLatencyMillis(currentTime),
- connection->getDispatchLatencyMillis(currentTime));
+ connection->getDispatchLatencyMillis(currentTime),
+ toString(handled));
#endif
if (connection->status == Connection::STATUS_BROKEN
@@ -1784,9 +1785,6 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
return;
}
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection);
-
// Reset the publisher since the event has been consumed.
// We do this now so that the publisher can release some of its internal resources
// while waiting for the next dispatch cycle to begin.
@@ -1798,7 +1796,8 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
return;
}
- startNextDispatchCycleLocked(currentTime, connection);
+ // Notify other system components and prepare to start the next dispatch cycle.
+ onDispatchCycleFinishedLocked(currentTime, connection, handled);
}
void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
@@ -1898,7 +1897,8 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
return 1;
}
- status_t status = connection->inputPublisher.receiveFinishedSignal();
+ bool handled = false;
+ status_t status = connection->inputPublisher.receiveFinishedSignal(handled);
if (status) {
LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
connection->getInputChannelName(), status);
@@ -1907,7 +1907,7 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
return 0; // remove the callback
}
- d->finishDispatchCycleLocked(currentTime, connection);
+ d->finishDispatchCycleLocked(currentTime, connection, handled);
d->runCommandsLockedInterruptible();
return 1;
} // release lock
@@ -2945,7 +2945,11 @@ void InputDispatcher::onDispatchCycleStartedLocked(
}
void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection) {
+ nsecs_t currentTime, const sp<Connection>& connection, bool handled) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
+ commandEntry->connection = connection;
+ commandEntry->handled = handled;
}
void InputDispatcher::onDispatchCycleBrokenLocked(
@@ -3014,9 +3018,7 @@ void InputDispatcher::doNotifyANRLockedInterruptible(
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
- mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
- entry->downTime, entry->eventTime);
+ initializeKeyEvent(&mReusableKeyEvent, entry);
mLock.unlock();
@@ -3031,6 +3033,31 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
mAllocator.releaseKeyEntry(entry);
}
+void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+ bool handled = commandEntry->handled;
+
+ if (!handled && !connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (dispatchEntry->inProgress
+ && dispatchEntry->hasForegroundTarget()
+ && dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+ initializeKeyEvent(&mReusableKeyEvent, keyEntry);
+
+ mLock.unlock();
+
+ mPolicy->dispatchUnhandledKey(connection->inputChannel,
+ & mReusableKeyEvent, keyEntry->policyFlags);
+
+ mLock.lock();
+ }
+ }
+
+ startNextDispatchCycleLocked(now(), connection);
+}
+
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
@@ -3039,6 +3066,12 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman
mLock.lock();
}
+void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
+ event->initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
+}
+
void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 2c6346e..1885691 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -35,8 +35,12 @@ static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
static const char INPUT_SIGNAL_DISPATCH = 'D';
// Signal sent by the consumer to the producer to inform it that it has finished
-// consuming the most recent message.
-static const char INPUT_SIGNAL_FINISHED = 'f';
+// consuming the most recent message and it handled it.
+static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f';
+
+// Signal sent by the consumer to the producer to inform it that it has finished
+// consuming the most recent message but it did not handle it.
+static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u';
// --- InputChannel ---
@@ -497,7 +501,7 @@ status_t InputPublisher::sendDispatchSignal() {
return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
}
-status_t InputPublisher::receiveFinishedSignal() {
+status_t InputPublisher::receiveFinishedSignal(bool& outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
@@ -506,9 +510,14 @@ status_t InputPublisher::receiveFinishedSignal() {
char signal;
status_t result = mChannel->receiveSignal(& signal);
if (result) {
+ outHandled = false;
return result;
}
- if (signal != INPUT_SIGNAL_FINISHED) {
+ if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
+ outHandled = true;
+ } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
+ outHandled = false;
+ } else {
LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
mChannel->getName().string(), signal);
return UNKNOWN_ERROR;
@@ -626,13 +635,15 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent*
return OK;
}
-status_t InputConsumer::sendFinishedSignal() {
+status_t InputConsumer::sendFinishedSignal(bool handled) {
#if DEBUG_TRANSPORT_ACTIONS
- LOGD("channel '%s' consumer ~ sendFinishedSignal",
- mChannel->getName().string());
+ LOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d",
+ mChannel->getName().string(), handled);
#endif
- return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);
+ return mChannel->sendSignal(handled
+ ? INPUT_SIGNAL_FINISHED_HANDLED
+ : INPUT_SIGNAL_FINISHED_UNHANDLED);
}
status_t InputConsumer::receiveDispatchSignal() {
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
index 8874dfe..f352dbf 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -67,6 +67,11 @@ private:
return false;
}
+ virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags) {
+ return false;
+ }
+
virtual void notifySwitch(nsecs_t when,
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {
}
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index 952b974..c6eac25 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -118,13 +118,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
EXPECT_EQ(downTime, keyEvent->getDownTime());
EXPECT_EQ(eventTime, keyEvent->getEventTime());
- status = mConsumer->sendFinishedSignal();
+ status = mConsumer->sendFinishedSignal(true);
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- status = mPublisher->receiveFinishedSignal();
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(handled);
ASSERT_EQ(OK, status)
<< "publisher receiveFinishedSignal should return OK";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
status = mPublisher->reset();
ASSERT_EQ(OK, status)
@@ -279,13 +282,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i));
}
- status = mConsumer->sendFinishedSignal();
+ status = mConsumer->sendFinishedSignal(false);
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- status = mPublisher->receiveFinishedSignal();
+ bool handled = true;
+ status = mPublisher->receiveFinishedSignal(handled);
ASSERT_EQ(OK, status)
<< "publisher receiveFinishedSignal should return OK";
+ ASSERT_FALSE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
status = mPublisher->reset();
ASSERT_EQ(OK, status)
diff --git a/native/android/input.cpp b/native/android/input.cpp
index c753aa5..a96240c 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -271,5 +271,5 @@ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event) {
}
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
- queue->finishEvent(event, handled != 0);
+ queue->finishEvent(event, handled != 0, false);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 30aab47..4bc7433 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -253,13 +253,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
@Override
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
- finishedCallback.run();
-
- synchronized (mLock) {
- if (mPointerLocationView != null) {
- mPointerLocationView.addTouchEvent(event);
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ boolean handled = false;
+ try {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ synchronized (mLock) {
+ if (mPointerLocationView != null) {
+ mPointerLocationView.addTouchEvent(event);
+ handled = true;
+ }
+ }
}
+ } finally {
+ finishedCallback.finished(handled);
}
}
};
@@ -1329,6 +1335,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return false;
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean dispatchUnhandledKey(WindowState win, int action, int flags,
+ int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
+ if (false) {
+ Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + action
+ + ", flags=" + flags + ", keyCode=" + keyCode
+ + ", scanCode=" + scanCode + ", metaState=" + metaState
+ + ", repeatCount=" + repeatCount + ", policyFlags=" + policyFlags);
+ }
+ return false;
+ }
+
/**
* A home key -> launch home action was detected. Take the appropriate action
* given the situation with the keyguard.
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 13be984..4364c04 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -419,6 +419,14 @@ public class InputManager {
}
@SuppressWarnings("unused")
+ public boolean dispatchUnhandledKey(InputChannel focus, int action,
+ int flags, int keyCode, int scanCode, int metaState, int repeatCount,
+ int policyFlags) {
+ return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(focus,
+ action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ }
+
+ @SuppressWarnings("unused")
public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
return mContext.checkPermission(
android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index c74a27c..1c92da9 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -824,13 +824,15 @@ public class WindowManagerService extends IWindowManager.Stub
DragState mDragState = null;
private final InputHandler mDragInputHandler = new BaseInputHandler() {
@Override
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
- boolean endDrag = false;
- final float newX = event.getRawX();
- final float newY = event.getRawY();
-
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ boolean handled = false;
try {
- if (mDragState != null) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
+ && mDragState != null) {
+ boolean endDrag = false;
+ final float newX = event.getRawX();
+ final float newY = event.getRawY();
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (DEBUG_DRAG) {
@@ -866,11 +868,13 @@ public class WindowManagerService extends IWindowManager.Stub
mDragState.endDragLw();
}
}
+
+ handled = true;
}
} catch (Exception e) {
Slog.e(TAG, "Exception caught by drag handleMotion", e);
} finally {
- finishedCallback.run();
+ finishedCallback.finished(handled);
}
}
};
@@ -5781,6 +5785,16 @@ public class WindowManagerService extends IWindowManager.Stub
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
+ /* Provides an opportunity for the window manager policy to process a key that
+ * the application did not handle. */
+ public boolean dispatchUnhandledKey(InputChannel focus,
+ int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
+ int policyFlags) {
+ WindowState windowState = getWindowStateForInputChannel(focus);
+ return mPolicy.dispatchUnhandledKey(windowState, action, flags,
+ keyCode, scanCode, metaState, repeatCount, policyFlags);
+ }
+
/* Called when the current input focus changes.
* Layer assignment is assumed to be complete by the time this is called.
*/
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index d4c4ba4..3fd6965 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -52,6 +52,7 @@ static struct {
jmethodID notifyANR;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptKeyBeforeDispatching;
+ jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
@@ -206,6 +207,8 @@ public:
virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags);
+ virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags);
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid);
@@ -937,6 +940,29 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i
}
}
+bool NativeInputManager::dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags) {
+ // Policy:
+ // - Ignore untrusted events and do not perform default handling.
+ if (policyFlags & POLICY_FLAG_TRUSTED) {
+ JNIEnv* env = jniEnv();
+
+ // Note: inputChannel may be null.
+ jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
+ jboolean handled = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.dispatchUnhandledKey,
+ inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+
+ env->DeleteLocalRef(inputChannelObj);
+ return handled && ! error;
+ } else {
+ return false;
+ }
+}
+
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
android_server_PowerManagerService_userActivity(eventTime, eventType);
}
@@ -1363,6 +1389,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
"interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z");
+ GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz,
+ "dispatchUnhandledKey", "(Landroid/view/InputChannel;IIIIIII)Z");
+
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");