summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcore/java/android/view/InputEvent.java9
-rw-r--r--core/java/android/view/WindowManagerPolicy.java2
-rw-r--r--include/ui/Input.h6
-rw-r--r--services/input/InputDispatcher.cpp81
-rw-r--r--services/input/InputDispatcher.h23
-rw-r--r--services/input/tests/InputDispatcher_test.cpp28
-rw-r--r--services/input/tests/InputReader_test.cpp7
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java86
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java24
-rw-r--r--services/java/com/android/server/wm/InputFilter.java231
-rw-r--r--services/java/com/android/server/wm/InputManager.java89
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/jni/com_android_server_InputManager.cpp58
13 files changed, 611 insertions, 37 deletions
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index f6aeb39..03189ca 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -67,6 +67,15 @@ public abstract class InputEvent implements Parcelable {
*/
public abstract void setSource(int source);
+ /**
+ * Recycles the event.
+ * This method should only be used by the system since applications do not
+ * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
+ * objects are fine. See {@link KeyEvent#recycle()} for details.
+ * @hide
+ */
+ public abstract void recycle();
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 334c68e..9d00d02 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -81,6 +81,8 @@ public interface WindowManagerPolicy {
public final static int FLAG_INJECTED = 0x01000000;
public final static int FLAG_TRUSTED = 0x02000000;
+ public final static int FLAG_FILTERED = 0x04000000;
+ public final static int FLAG_DISABLE_KEY_REPEAT = 0x08000000;
public final static int FLAG_WOKE_HERE = 0x10000000;
public final static int FLAG_BRIGHT_HERE = 0x20000000;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8e8b61b..b22986d 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -128,6 +128,12 @@ enum {
// input device or an application with system-wide event injection permission.
POLICY_FLAG_TRUSTED = 0x02000000,
+ // Indicates that the input event has passed through an input filter.
+ POLICY_FLAG_FILTERED = 0x04000000,
+
+ // Disables automatic key repeating behavior.
+ POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,
+
/* These flags are set by the input reader policy as it intercepts each event. */
// Indicates that the screen was off when the event was received and the event
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 6ea068a..8363e8b 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -186,7 +186,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
mPolicy(policy),
mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
- mDispatchEnabled(true), mDispatchFrozen(false),
+ mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false),
mFocusedWindow(NULL),
mFocusedApplication(NULL),
mCurrentInputTargetsValid(false),
@@ -725,7 +725,7 @@ bool InputDispatcher::dispatchKeyLocked(
if (entry->repeatCount == 0
&& entry->action == AKEY_EVENT_ACTION_DOWN
&& (entry->policyFlags & POLICY_FLAG_TRUSTED)
- && !entry->isInjected()) {
+ && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
@@ -2402,7 +2402,18 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ mLock.lock();
+
+ if (mInputFilterEnabled) {
+ mLock.unlock();
+
+ policyFlags |= POLICY_FLAG_FILTERED;
+ if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ return; // event was consumed by the filter
+ }
+
+ mLock.lock();
+ }
int32_t repeatCount = 0;
KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
@@ -2410,6 +2421,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so
metaState, repeatCount, downTime);
needWake = enqueueInboundEventLocked(newEntry);
+ mLock.unlock();
} // release lock
if (needWake) {
@@ -2452,7 +2464,23 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ mLock.lock();
+
+ if (mInputFilterEnabled) {
+ mLock.unlock();
+
+ MotionEvent event;
+ event.initialize(deviceId, source, action, flags, edgeFlags, metaState, 0, 0,
+ xPrecision, yPrecision, downTime, eventTime,
+ pointerCount, pointerIds, pointerCoords);
+
+ policyFlags |= POLICY_FLAG_FILTERED;
+ if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ return; // event was consumed by the filter
+ }
+
+ mLock.lock();
+ }
// Attempt batching and streaming of move events.
if (action == AMOTION_EVENT_ACTION_MOVE
@@ -2491,6 +2519,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
LOGD("Appended motion sample onto batch for most recent "
"motion event for this device in the inbound queue.");
#endif
+ mLock.unlock();
return; // done!
}
@@ -2579,6 +2608,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
true /*resumeWithAppendedMotionSample*/);
runCommandsLockedInterruptible();
+ mLock.unlock();
return; // done!
}
}
@@ -2593,6 +2623,7 @@ NoBatchingOrStreaming:;
pointerCount, pointerIds, pointerCoords);
needWake = enqueueInboundEventLocked(newEntry);
+ mLock.unlock();
} // release lock
if (needWake) {
@@ -2612,16 +2643,17 @@ void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t swi
}
int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
+ "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
- uint32_t policyFlags = POLICY_FLAG_INJECTED;
+ policyFlags |= POLICY_FLAG_INJECTED;
if (hasInjectionPermission(injectorPid, injectorUid)) {
policyFlags |= POLICY_FLAG_TRUSTED;
}
@@ -2640,7 +2672,9 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
policyFlags |= POLICY_FLAG_VIRTUAL;
}
- mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+ }
if (policyFlags & POLICY_FLAG_WOKE_HERE) {
flags |= AKEY_EVENT_FLAG_WOKE_HERE;
@@ -2664,8 +2698,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
return INPUT_EVENT_INJECTION_FAILED;
}
- nsecs_t eventTime = motionEvent->getEventTime();
- mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ nsecs_t eventTime = motionEvent->getEventTime();
+ mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+ }
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -2780,7 +2816,8 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject
injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (injectionState->injectionIsAsync) {
+ if (injectionState->injectionIsAsync
+ && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2982,6 +3019,26 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
}
}
+void InputDispatcher::setInputFilterEnabled(bool enabled) {
+#if DEBUG_FOCUS
+ LOGD("setInputFilterEnabled: enabled=%d", enabled);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mInputFilterEnabled == enabled) {
+ return;
+ }
+
+ mInputFilterEnabled = enabled;
+ resetAndDropEverythingLocked("input filter is being enabled or disabled");
+ } // release lock
+
+ // Wake up poll loop since there might be work to do to drop everything.
+ mLooper->wake();
+}
+
bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel) {
#if DEBUG_FOCUS
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 48e4d43..162e606 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -176,6 +176,13 @@ public:
*/
virtual int32_t getMaxEventsPerSecond() = 0;
+ /* Filters an input event.
+ * Return true to dispatch the event unmodified, false to consume the event.
+ * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
+ * to injectInputEvent.
+ */
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+
/* Intercepts a key event immediately before queueing it.
* The policy can use this method as an opportunity to perform power management functions
* and early event preprocessing such as updating policy flags.
@@ -266,7 +273,8 @@ public:
* This method may be called on any thread (usually by the input manager).
*/
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) = 0;
/* Sets the list of input windows.
*
@@ -286,6 +294,14 @@ public:
*/
virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+ /* Sets whether input event filtering is enabled.
+ * When enabled, incoming input events are sent to the policy's filterInputEvent
+ * method instead of being dispatched. The filter is expected to use
+ * injectInputEvent to inject the events it would like to have dispatched.
+ * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
+ */
+ virtual void setInputFilterEnabled(bool enabled) = 0;
+
/* Transfers touch focus from the window associated with one channel to the
* window associated with the other channel.
*
@@ -345,11 +361,13 @@ public:
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) ;
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags);
virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
virtual void setFocusedApplication(const InputApplication* inputApplication);
virtual void setInputDispatchMode(bool enabled, bool frozen);
+ virtual void setInputFilterEnabled(bool enabled);
virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel);
@@ -863,6 +881,7 @@ private:
// Dispatch state.
bool mDispatchEnabled;
bool mDispatchFrozen;
+ bool mInputFilterEnabled;
Vector<InputWindow> mWindows;
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 2f846c4..3650da0 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -67,6 +67,10 @@ private:
return 60;
}
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ return true;
+ }
+
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
}
@@ -124,7 +128,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
/*action*/ -1, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
@@ -132,7 +136,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
AKEY_EVENT_ACTION_MULTIPLE, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -150,7 +154,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
@@ -160,7 +164,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -169,7 +173,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
@@ -179,7 +183,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -188,7 +192,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
@@ -197,7 +201,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -205,7 +209,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
@@ -215,7 +219,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerIds[0] = MAX_POINTER_ID + 1;
@@ -224,7 +228,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -235,7 +239,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 60549c6..4c5f239 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -369,7 +369,8 @@ private:
}
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) {
ADD_FAILURE() << "Should never be called by input reader.";
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -386,6 +387,10 @@ private:
ADD_FAILURE() << "Should never be called by input reader.";
}
+ virtual void setInputFilterEnabled(bool enabled) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel) {
ADD_FAILURE() << "Should never be called by input reader.";
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
new file mode 100644
index 0000000..ced8feb
--- /dev/null
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Input filter for accessibility.
+ *
+ * Currently just a stub but will eventually implement touch exploration, etc.
+ */
+public class AccessibilityInputFilter extends InputFilter {
+ private static final String TAG = "AccessibilityInputFilter";
+ private static final boolean DEBUG = true;
+
+ private final Context mContext;
+
+ public AccessibilityInputFilter(Context context) {
+ super(context.getMainLooper());
+ mContext = context;
+ }
+
+ @Override
+ public void onInstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter installed.");
+ }
+ super.onInstalled();
+ }
+
+ @Override
+ public void onUninstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter uninstalled.");
+ }
+ super.onUninstalled();
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter received input event: "
+ + event + ", policyFlags=0x" + Integer.toHexString(policyFlags));
+ }
+
+ // To prove that this is working as intended, we will silently transform
+ // Q key presses into non-repeating Z's as part of this stub implementation.
+ // TODO: Replace with the real thing.
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_Q) {
+ if (keyEvent.getRepeatCount() == 0) {
+ sendInputEvent(new KeyEvent(keyEvent.getDownTime(), keyEvent.getEventTime(),
+ keyEvent.getAction(), KeyEvent.KEYCODE_Z, keyEvent.getRepeatCount(),
+ keyEvent.getMetaState(), keyEvent.getDeviceId(), keyEvent.getScanCode(),
+ keyEvent.getFlags(), keyEvent.getSource()),
+ policyFlags | WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
+ }
+ return;
+ }
+ }
+
+ super.onInputEvent(event, policyFlags);
+ }
+}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index ba74d86..5257fb0 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.accessibility;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -43,6 +44,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -106,6 +108,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private int mHandledFeedbackTypes = 0;
private boolean mIsEnabled;
+ private AccessibilityInputFilter mInputFilter;
/**
* Handler for delayed event dispatch.
@@ -209,6 +212,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
manageServicesLocked();
+ updateInputFilterLocked();
}
return;
@@ -249,6 +253,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
unbindAllServicesLocked();
}
updateClientsLocked();
+ updateInputFilterLocked();
}
}
});
@@ -621,6 +626,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
+ * Installs or removes the accessibility input filter when accessibility is enabled
+ * or disabled.
+ */
+ private void updateInputFilterLocked() {
+ WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
+ Context.WINDOW_SERVICE);
+ if (wm != null) {
+ if (mIsEnabled) {
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext);
+ }
+ wm.setInputFilter(mInputFilter);
+ } else {
+ wm.setInputFilter(null);
+ }
+ }
+ }
+
+ /**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
* service and is responsible for adding/removing the service in the data structures
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
new file mode 100644
index 0000000..78b87fe
--- /dev/null
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Filters input events before they are dispatched to the system.
+ * <p>
+ * At most one input filter can be installed by calling
+ * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the
+ * system's behavior changes as follows:
+ * <ul>
+ * <li>Input events are first delivered to the {@link WindowManagerPolicy}
+ * interception methods before queueing as usual. This critical step takes care of managing
+ * the power state of the device and handling wake keys.</li>
+ * <li>Input events are then asynchronously delivered to the input filter's
+ * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
+ * applications as usual. The input filter only receives input events that were
+ * generated by input device; the input filter will not receive input events that were
+ * injected into the system by other means, such as by instrumentation.</li>
+ * <li>The input filter processes and optionally transforms the stream of events. For example,
+ * it may transform a sequence of motion events representing an accessibility gesture into
+ * a different sequence of motion events, key presses or other system-level interactions.
+ * The input filter can send events to be dispatched by calling
+ * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
+ * input event.</li>
+ * </ul>
+ * </p>
+ * <h3>The importance of input event consistency</h3>
+ * <p>
+ * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it
+ * sends an internally consistent stream of input events to the dispatcher. There are
+ * very important invariants to be maintained.
+ * </p><p>
+ * For example, if a key down is sent, a corresponding key up should also be sent eventually.
+ * Likewise, for touch events, each pointer must individually go down with
+ * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
+ * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
+ * and the sequence of pointer ids used must be consistent throughout the gesture.
+ * </p><p>
+ * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should
+ * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
+ * </p><p>
+ * The input filter must take into account the fact that the input events coming from different
+ * devices or even different sources all consist of distinct streams of input.
+ * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
+ * the source of the event and its semantics. There are be multiple sources of keys,
+ * touches and other input: they must be kept separate.
+ * </p>
+ * <h3>Policy flags</h3>
+ * <p>
+ * Input events received from the dispatcher and sent to the dispatcher have policy flags
+ * associated with them. Policy flags control some functions of the dispatcher.
+ * </p><p>
+ * The early policy interception decides whether an input event should be delivered
+ * to applications or dropped. The policy indicates its decision by setting the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag. The input filter may
+ * sometimes receive events that do not have this flag set. It should take note of
+ * the fact that the policy intends to drop the event, clean up its state, and
+ * then send appropriate cancelation events to the dispatcher if needed.
+ * </p><p>
+ * For example, suppose the input filter is processing a gesture and one of the touch events
+ * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set.
+ * The input filter should clear its internal state about the gesture and then send key or
+ * motion events to the dispatcher to cancel any keys or pointers that are down.
+ * </p><p>
+ * Corollary: Events that set sent to the dispatcher should usually include the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
+ * </p><p>
+ * It may be prudent to disable automatic key repeating for synthetically generated
+ * keys by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag.
+ * </p>
+ */
+public abstract class InputFilter {
+ private static final int MSG_INSTALL = 1;
+ private static final int MSG_UNINSTALL = 2;
+ private static final int MSG_INPUT_EVENT = 3;
+
+ private final H mH;
+ private Host mHost;
+
+ /**
+ * Creates the input filter.
+ *
+ * @param looper The looper to run callbacks on.
+ */
+ public InputFilter(Looper looper) {
+ mH = new H(looper);
+ }
+
+ /**
+ * Called when the input filter is installed.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param host The input filter host environment.
+ */
+ final void install(Host host) {
+ mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
+ }
+
+ /**
+ * Called when the input filter is uninstalled.
+ * This method is guaranteed to be non-reentrant.
+ */
+ final void uninstall() {
+ mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
+ }
+
+ /**
+ * Called to enqueue the input event for filtering.
+ * The event will be recycled after the input filter processes it.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param event The input event to enqueue.
+ */
+ final void filterInputEvent(InputEvent event, int policyFlags) {
+ mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
+ }
+
+ /**
+ * Sends an input event to the dispatcher.
+ *
+ * @param event The input event to publish.
+ * @param policyFlags The input event policy flags.
+ */
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (mHost == null) {
+ throw new IllegalStateException("Cannot send input event because the input filter " +
+ "is not installed.");
+ }
+ mHost.sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when an input event has been received from the dispatcher.
+ * <p>
+ * The default implementation sends the input event back to the dispatcher, unchanged.
+ * </p><p>
+ * The event will be recycled when this method returns. If you want to keep it around,
+ * make a copy!
+ * </p>
+ *
+ * @param event The input event that was received.
+ * @param policyFlags The input event policy flags.
+ */
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when the filter is installed into the dispatch pipeline.
+ * <p>
+ * This method is called before the input filter receives any input events.
+ * The input filter should take this opportunity to prepare itself.
+ * </p>
+ */
+ public void onInstalled() {
+ }
+
+ /**
+ * Called when the filter is uninstalled from the dispatch pipeline.
+ * <p>
+ * This method is called after the input filter receives its last input event.
+ * The input filter should take this opportunity to clean up.
+ * </p>
+ */
+ public void onUninstalled() {
+ }
+
+ private final class H extends Handler {
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INSTALL:
+ mHost = (Host)msg.obj;
+ onInstalled();
+ break;
+
+ case MSG_UNINSTALL:
+ try {
+ onUninstalled();
+ } finally {
+ mHost = null;
+ }
+ break;
+
+ case MSG_INPUT_EVENT: {
+ final InputEvent event = (InputEvent)msg.obj;
+ try {
+ onInputEvent(event, msg.arg1);
+ } finally {
+ event.recycle();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ interface Host {
+ public void sendInputEvent(InputEvent event, int policyFlags);
+ }
+}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ca1da95..b0978a3 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -42,6 +42,7 @@ import android.view.KeyEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
import java.io.File;
import java.io.FileNotFoundException;
@@ -78,8 +79,10 @@ public class InputManager {
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle, boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+ private static native void nativeSetInputFilterEnabled(boolean enable);
private static native int nativeInjectInputEvent(InputEvent event,
- int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
+ int policyFlags);
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(int visibility);
@@ -117,6 +120,11 @@ public class InputManager {
/** The key is down but is a virtual key press that is being emulated by the system. */
public static final int KEY_STATE_VIRTUAL = 2;
+ // State for the currently installed input filter.
+ final Object mInputFilterLock = new Object();
+ InputFilter mInputFilter;
+ InputFilterHost mInputFilterHost;
+
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
@@ -268,7 +276,42 @@ public class InputManager {
nativeUnregisterInputChannel(inputChannel);
}
-
+
+ /**
+ * Sets an input filter that will receive all input events before they are dispatched.
+ * The input filter may then reinterpret input events or inject new ones.
+ *
+ * To ensure consistency, the input dispatcher automatically drops all events
+ * in progress whenever an input filter is installed or uninstalled. After an input
+ * filter is uninstalled, it can no longer send input events unless it is reinstalled.
+ * Any events it attempts to send after it has been uninstalled will be dropped.
+ *
+ * @param filter The input filter, or null to remove the current filter.
+ */
+ public void setInputFilter(InputFilter filter) {
+ synchronized (mInputFilterLock) {
+ final InputFilter oldFilter = mInputFilter;
+ if (oldFilter == filter) {
+ return; // nothing to do
+ }
+
+ if (oldFilter != null) {
+ mInputFilter = null;
+ mInputFilterHost.disconnectLocked();
+ mInputFilterHost = null;
+ oldFilter.uninstall();
+ }
+
+ if (filter != null) {
+ mInputFilter = filter;
+ mInputFilterHost = new InputFilterHost();
+ filter.install(mInputFilterHost);
+ }
+
+ nativeSetInputFilterEnabled(filter != null);
+ }
+ }
+
/**
* Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for
@@ -304,9 +347,10 @@ public class InputManager {
throw new IllegalArgumentException("timeoutMillis must be positive");
}
- return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis,
+ WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
}
-
+
/**
* Gets information about the input device with the specified id.
* @param id The device id.
@@ -370,6 +414,27 @@ public class InputManager {
}
}
+ private final class InputFilterHost implements InputFilter.Host {
+ private boolean mDisconnected;
+
+ public void disconnectLocked() {
+ mDisconnected = true;
+ }
+
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+
+ synchronized (mInputFilterLock) {
+ if (!mDisconnected) {
+ nativeInjectInputEvent(event, 0, 0, INPUT_EVENT_INJECTION_SYNC_NONE, 0,
+ policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+ }
+ }
+ }
+ }
+
private static final class PointerIcon {
public Bitmap bitmap;
public float hotSpotX;
@@ -415,7 +480,7 @@ public class InputManager {
/*
* Callbacks from native.
*/
- private class Callbacks {
+ private final class Callbacks {
static final String TAG = "InputManager-Callbacks";
private static final boolean DEBUG_VIRTUAL_KEYS = false;
@@ -443,7 +508,19 @@ public class InputManager {
return mWindowManagerService.mInputMonitor.notifyANR(
inputApplicationHandle, inputWindowHandle);
}
-
+
+ @SuppressWarnings("unused")
+ final boolean filterInputEvent(InputEvent event, int policyFlags) {
+ synchronized (mInputFilterLock) {
+ if (mInputFilter != null) {
+ mInputFilter.filterInputEvent(event, policyFlags);
+ return false;
+ }
+ }
+ event.recycle();
+ return true;
+ }
+
@SuppressWarnings("unused")
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 33e6a36..79c4518 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4604,6 +4604,10 @@ public class WindowManagerService extends IWindowManager.Stub
return mInputManager.monitorInput(inputChannelName);
}
+ public void setInputFilter(InputFilter filter) {
+ mInputManager.setInputFilter(filter);
+ }
+
public InputDevice getInputDevice(int deviceId) {
return mInputManager.getInputDevice(deviceId);
}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 96cf4bd..ab2c125 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,6 +56,7 @@ static struct {
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
+ jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingWhenScreenOff;
jmethodID interceptKeyBeforeDispatching;
@@ -174,6 +175,7 @@ public:
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
virtual int32_t getMaxEventsPerSecond();
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
@@ -638,6 +640,38 @@ bool NativeInputManager::isScreenBright() {
return android_server_PowerManagerService_isScreenBright();
}
+bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ jobject inputEventObj;
+
+ JNIEnv* env = jniEnv();
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY:
+ inputEventObj = android_view_KeyEvent_fromNative(env,
+ static_cast<const KeyEvent*>(inputEvent));
+ break;
+ case AINPUT_EVENT_TYPE_MOTION:
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<const MotionEvent*>(inputEvent));
+ break;
+ default:
+ return true; // dispatch the event normally
+ }
+
+ if (!inputEventObj) {
+ LOGE("Failed to obtain input event object for filterInputEvent.");
+ return true; // dispatch the event normally
+ }
+
+ // The callee is responsible for recycling the event.
+ jboolean pass = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterInputEvent,
+ inputEventObj, policyFlags);
+ if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
+ pass = true;
+ }
+ env->DeleteLocalRef(inputEventObj);
+ return pass;
+}
+
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
// Policy:
@@ -1005,9 +1039,18 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env
}
}
+static void android_server_InputManager_nativeSetInputFilterEnabled(JNIEnv* env, jclass clazz,
+ jboolean enabled) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled);
+}
+
static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz,
jobject inputEventObj, jint injectorPid, jint injectorUid,
- jint syncMode, jint timeoutMillis) {
+ jint syncMode, jint timeoutMillis, jint policyFlags) {
if (checkInputManagerUnitialized(env)) {
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -1021,7 +1064,8 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla
}
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
- & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+ & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+ uint32_t(policyFlags));
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
if (!motionEvent) {
@@ -1030,7 +1074,8 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla
}
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
- motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+ motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+ uint32_t(policyFlags));
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
@@ -1200,7 +1245,9 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
- { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
+ { "nativeSetInputFilterEnabled", "(Z)V",
+ (void*) android_server_InputManager_nativeSetInputFilterEnabled },
+ { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIIII)I",
(void*) android_server_InputManager_nativeInjectInputEvent },
{ "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V",
(void*) android_server_InputManager_nativeSetInputWindows },
@@ -1257,6 +1304,9 @@ int register_android_server_InputManager(JNIEnv* env) {
"notifyANR",
"(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J");
+ GET_METHOD_ID(gCallbacksClassInfo.filterInputEvent, clazz,
+ "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
+
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");