summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Prabir Pradhan <prabirmsp@google.com> 2022-03-08 21:05:57 +0000
committer Prabir Pradhan <prabirmsp@google.com> 2022-04-04 17:18:05 +0000
commit985a1b2d7974265ef7ec57fd2a3f96089abf2d2e (patch)
tree9d3ddb02a571d8b466b924a7f6dba32de6f901f6
parentf7c6df18ec8dcdae0d07fe5be32347ee83cdb966 (diff)
Change input injection security model to require INJECT_EVENTS permission
Previously, any app could inject input events into the system via the IInputManager#injectInputEvent API. The injection was only allowed if the input event targeted a window owned by the same UID as that of the process calling the API. This had drawbacks metioned in the bug. Here, we change the input injection security model so that the signature permission INJECT_EVENTS is required to inject events. This permission is given to the system and the shell, so input injection can still be done through the 'adb shell input' command. We also allow injection from instrumeted processes where the instrumentation source has the permission. For exmaple, running a test from the shell allows for the test to inject events. We also add support for a targeted injection mode, where the input injection succeeds only if the target window for the event is owned by the provided UID. This allows us to support injection from the Instrumentation class, which only allows for injection into windows owned by the same UID. In contrast to this, injection from the UiAutomation class will target all windows, including system and spy windows. Bug: 207667844 Bug: 194952792 Test: atest inputflinger_tests Test: atest WindowInputTests Test: manual with test app: app cannot inject navigation gestures Change-Id: Ie2d8d0c3d784d389335401e148bf394d817bfd60
-rw-r--r--libs/input/android/os/InputEventInjectionResult.aidl5
-rw-r--r--services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp22
-rw-r--r--services/inputflinger/dispatcher/InjectionState.cpp5
-rw-r--r--services/inputflinger/dispatcher/InjectionState.h5
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp168
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h5
-rw-r--r--services/inputflinger/dispatcher/include/InputDispatcherInterface.h10
-rw-r--r--services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h9
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp249
9 files changed, 309 insertions, 169 deletions
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
index 34f10ec00e..3bc7068f3c 100644
--- a/libs/input/android/os/InputEventInjectionResult.aidl
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -29,9 +29,8 @@ enum InputEventInjectionResult {
/* Injection succeeded. */
SUCCEEDED = 0,
- /* Injection failed because the injector did not have permission to inject
- * into the application with input focus. */
- PERMISSION_DENIED = 1,
+ /* Injection failed because the injected event did not target the appropriate window. */
+ TARGET_MISMATCH = 1,
/* Injection failed because there were no available input targets. */
FAILED = 2,
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 32eec291cb..a2e60c4e6f 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -31,11 +31,11 @@ using android::os::InputEventInjectionSync;
namespace android::inputdispatcher {
// An arbitrary device id.
-static const int32_t DEVICE_ID = 1;
+constexpr int32_t DEVICE_ID = 1;
-// An arbitrary injector pid / uid pair that has permission to inject events.
-static const int32_t INJECTOR_PID = 999;
-static const int32_t INJECTOR_UID = 1001;
+// The default pid and uid for windows created by the test.
+constexpr int32_t WINDOW_PID = 999;
+constexpr int32_t WINDOW_UID = 1001;
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
@@ -108,8 +108,6 @@ private:
void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
- bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
-
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
void setPointerCapture(const PointerCaptureRequest&) override {}
@@ -196,8 +194,8 @@ public:
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(mFrame);
- mInfo.ownerPid = INJECTOR_PID;
- mInfo.ownerUid = INJECTOR_UID;
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
mInfo.displayId = ADISPLAY_ID_DEFAULT;
}
@@ -310,14 +308,14 @@ static void benchmarkInjectMotion(benchmark::State& state) {
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
- dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+ dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
// Send ACTION_UP
event.setAction(AMOTION_EVENT_ACTION_UP);
- dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+ dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index c8024a61a9..c2d3ad6b9d 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,10 +20,9 @@
namespace android::inputdispatcher {
-InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid)
+InjectionState::InjectionState(const std::optional<int32_t>& targetUid)
: refCount(1),
- injectorPid(injectorPid),
- injectorUid(injectorUid),
+ targetUid(targetUid),
injectionResult(android::os::InputEventInjectionResult::PENDING),
injectionIsAsync(false),
pendingForegroundDispatches(0) {}
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 0bfafb1d19..90cf150ac3 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -27,13 +27,12 @@ namespace inputdispatcher {
struct InjectionState {
mutable int32_t refCount;
- int32_t injectorPid;
- int32_t injectorUid;
+ std::optional<int32_t> targetUid;
android::os::InputEventInjectionResult injectionResult; // initially PENDING
bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- InjectionState(int32_t injectorPid, int32_t injectorUid);
+ explicit InjectionState(const std::optional<int32_t>& targetUid);
void release();
private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 11cceaac93..f3d0b6539d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -578,6 +578,27 @@ bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int3
return false;
}
+// Checks targeted injection using the window's owner's uid.
+// Returns an empty string if an entry can be sent to the given window, or an error message if the
+// entry is a targeted injection whose uid target doesn't match the window owner.
+std::optional<std::string> verifyTargetedInjection(const sp<WindowInfoHandle>& window,
+ const EventEntry& entry) {
+ if (entry.injectionState == nullptr || !entry.injectionState->targetUid) {
+ // The event was not injected, or the injected event does not target a window.
+ return {};
+ }
+ const int32_t uid = *entry.injectionState->targetUid;
+ if (window == nullptr) {
+ return StringPrintf("No valid window target for injection into uid %d.", uid);
+ }
+ if (entry.injectionState->targetUid != window->getInfo()->ownerUid) {
+ return StringPrintf("Injected event targeted at uid %d would be dispatched to window '%s' "
+ "owned by uid %d.",
+ uid, window->getName().c_str(), window->getInfo()->ownerUid);
+ }
+ return {};
+}
+
} // namespace
// --- InputDispatcher ---
@@ -1036,6 +1057,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE
switch (entry.type) {
case EventEntry::Type::KEY: {
+ LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
+ "Unexpected untrusted event.");
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
@@ -1073,6 +1096,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE
}
case EventEntry::Type::MOTION: {
+ LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
+ "Unexpected untrusted event.");
if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
@@ -1718,8 +1743,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<
}
setInjectionResult(*entry, injectionResult);
- if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
- ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
+ if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) {
return true;
}
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
@@ -1976,9 +2000,10 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
// we have a valid, non-null focused window
resetNoFocusedWindowTimeoutLocked();
- // Check permissions.
- if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
- return InputEventInjectionResult::PERMISSION_DENIED;
+ // Verify targeted injection.
+ if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
+ ALOGW("Dropping injected event: %s", (*err).c_str());
+ return InputEventInjectionResult::TARGET_MISMATCH;
}
if (focusedWindowHandle->getInfo()->inputConfig.test(
@@ -2044,11 +2069,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
ATRACE_CALL();
- enum InjectionPermission {
- INJECTION_PERMISSION_UNKNOWN,
- INJECTION_PERMISSION_GRANTED,
- INJECTION_PERMISSION_DENIED
- };
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
@@ -2058,7 +2078,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
// Update the touch state as needed based on the properties of the touch event.
InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
- InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
sp<WindowInfoHandle> newTouchedWindowHandle;
@@ -2107,7 +2126,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
+ injectionResult = InputEventInjectionResult::FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -2140,6 +2159,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
+ // Verify targeted injection.
+ if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
+ ALOGW("Dropping injected touch event: %s", (*err).c_str());
+ injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ newTouchedWindowHandle = nullptr;
+ goto Failed;
+ }
+
// Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr) {
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2183,6 +2210,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
const WindowInfo& info = *windowHandle->getInfo();
+ // Skip spy window targets that are not valid for targeted injection.
+ if (const auto err = verifyTargetedInjection(windowHandle, entry); err) {
+ continue;
+ }
+
if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
ALOGI("Not sending touch event to %s because it is paused",
windowHandle->getName().c_str());
@@ -2276,6 +2308,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
+ // Verify targeted injection.
+ if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
+ ALOGW("Dropping injected event: %s", (*err).c_str());
+ injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ newTouchedWindowHandle = nullptr;
+ goto Failed;
+ }
+
// Drop touch events if requested by input feature
if (newTouchedWindowHandle != nullptr &&
shouldDropInput(entry, newTouchedWindowHandle)) {
@@ -2367,19 +2407,26 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
goto Failed;
}
- // Check permission to inject into all touched foreground windows.
- if (std::any_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
- [this, &entry](const TouchedWindow& touchedWindow) {
- return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 &&
- !checkInjectionPermission(touchedWindow.windowHandle,
- entry.injectionState);
- })) {
- injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- goto Failed;
+ // Ensure that all touched windows are valid for injection.
+ if (entry.injectionState != nullptr) {
+ std::string errs;
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
+ if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ // Allow ACTION_OUTSIDE events generated by targeted injection to be
+ // dispatched to any uid, since the coords will be zeroed out later.
+ continue;
+ }
+ const auto err = verifyTargetedInjection(touchedWindow.windowHandle, entry);
+ if (err) errs += "\n - " + *err;
+ }
+ if (!errs.empty()) {
+ ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
+ "%d:%s",
+ *entry.injectionState->targetUid, errs.c_str());
+ injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+ goto Failed;
+ }
}
- // Permission granted to inject into all touched foreground windows.
- injectionPermission = INJECTION_PERMISSION_GRANTED;
// Check whether windows listening for outside touches are owned by the same UID. If it is
// set the policy flag that we will not reveal coordinate information to this window.
@@ -2445,19 +2492,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
tempTouchState.filterNonAsIsTouchWindows();
Failed:
- // Check injection permission once and for all.
- if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(nullptr, entry.injectionState)) {
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- } else {
- injectionPermission = INJECTION_PERMISSION_DENIED;
- }
- }
-
- if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
- return injectionResult;
- }
-
// Update final pieces of touch state if the injector had permission.
if (!wrongDevice) {
if (switchedDevice) {
@@ -2655,26 +2689,6 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>&
}
}
-bool InputDispatcher::checkInjectionPermission(const sp<WindowInfoHandle>& windowHandle,
- const InjectionState* injectionState) {
- if (injectionState &&
- (windowHandle == nullptr ||
- windowHandle->getInfo()->ownerUid != injectionState->injectorUid) &&
- !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
- if (windowHandle != nullptr) {
- ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
- "owned by uid %d",
- injectionState->injectorPid, injectionState->injectorUid,
- windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid);
- } else {
- ALOGW("Permission denied: injecting event from pid %d uid %d",
- injectionState->injectorPid, injectionState->injectorUid);
- }
- return false;
- }
- return true;
-}
-
/**
* Indicate whether one window handle should be considered as obscuring
* another window handle. We only check a few preconditions. Actually
@@ -4193,20 +4207,20 @@ void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChan
}
}
-InputEventInjectionResult InputDispatcher::injectInputEvent(
- const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
- InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
+InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
+ std::optional<int32_t> targetUid,
+ InputEventInjectionSync syncMode,
+ std::chrono::milliseconds timeout,
+ uint32_t policyFlags) {
if (DEBUG_INBOUND_EVENT_DETAILS) {
- ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
- event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+ ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, "
+ "policyFlags=0x%08x",
+ event->getType(), targetUid ? std::to_string(*targetUid).c_str() : "none", syncMode,
+ timeout.count(), policyFlags);
}
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
- policyFlags |= POLICY_FLAG_INJECTED;
- if (hasInjectionPermission(injectorPid, injectorUid)) {
- policyFlags |= POLICY_FLAG_TRUSTED;
- }
+ policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED;
// For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
// that have gone through the InputFilter. If the event passed through the InputFilter, assign
@@ -4347,7 +4361,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
return InputEventInjectionResult::FAILED;
}
- InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
+ InjectionState* injectionState = new InjectionState(targetUid);
if (syncMode == InputEventInjectionSync::NONE) {
injectionState->injectionIsAsync = true;
}
@@ -4419,8 +4433,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
} // release lock
if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
- injectionResult, injectorPid, injectorUid);
+ ALOGD("injectInputEvent - Finished with result %d.", injectionResult);
}
return injectionResult;
@@ -4459,19 +4472,12 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu
return result;
}
-bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
- return injectorUid == 0 ||
- mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
-}
-
void InputDispatcher::setInjectionResult(EventEntry& entry,
InputEventInjectionResult injectionResult) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
if (DEBUG_INJECTION) {
- ALOGD("Setting input event injection result to %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+ ALOGD("Setting input event injection result to %d.", injectionResult);
}
if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -4480,12 +4486,12 @@ void InputDispatcher::setInjectionResult(EventEntry& entry,
case InputEventInjectionResult::SUCCEEDED:
ALOGV("Asynchronous input event injection succeeded.");
break;
+ case InputEventInjectionResult::TARGET_MISMATCH:
+ ALOGV("Asynchronous input event injection target mismatch.");
+ break;
case InputEventInjectionResult::FAILED:
ALOGW("Asynchronous input event injection failed.");
break;
- case InputEventInjectionResult::PERMISSION_DENIED:
- ALOGW("Asynchronous input event injection permission denied.");
- break;
case InputEventInjectionResult::TIMED_OUT:
ALOGW("Asynchronous input event injection timed out.");
break;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ca24f17375..a9fb5c6799 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -104,7 +104,7 @@ public:
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ const InputEvent* event, std::optional<int32_t> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
@@ -275,7 +275,6 @@ private:
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
- bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
void setInjectionResult(EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
void transformMotionEntryForInjectionLocked(MotionEntry&,
@@ -551,8 +550,6 @@ private:
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
- bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
- const InjectionState* injectionState);
// Enqueue a drag event if needed, and update the touch state.
// Uses findTouchedWindowTargetsLocked to make the decision
void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 67e1b6f93b..100bd186e7 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -68,10 +68,16 @@ public:
* input injection to proceed.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants.
*
- * This method may be called on any thread (usually by the input manager).
+ * If a targetUid is provided, InputDispatcher will only consider injecting the input event into
+ * windows owned by the provided uid. If the input event is targeted at a window that is not
+ * owned by the provided uid, input injection will fail. If no targetUid is provided, the input
+ * event will be dispatched as-is.
+ *
+ * This method may be called on any thread (usually by the input manager). The caller must
+ * perform all necessary permission checks prior to injecting events.
*/
virtual android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ const InputEvent* event, std::optional<int32_t> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) = 0;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index de0b6da884..575b3d7059 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -125,15 +125,6 @@ public:
/* Poke user activity for an event dispatched to a window. */
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
- /* Checks whether a given application pid/uid has permission to inject input events
- * into other applications.
- *
- * This method is special in that its implementation promises to be non-reentrant and
- * is safe to call while holding other locks. (Most other methods make no such guarantees!)
- */
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
- int32_t injectorUid) = 0;
-
/* Notifies the policy that a pointer down event has occurred outside the current focused
* window.
*
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 112b19a8fa..84ae31b6e2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -45,10 +45,10 @@ namespace android::inputdispatcher {
using namespace ftl::flag_operators;
// An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
// An arbitrary device id.
-static const int32_t DEVICE_ID = 1;
+static constexpr int32_t DEVICE_ID = 1;
// An arbitrary display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
@@ -59,9 +59,12 @@ static constexpr int32_t POINTER_1_DOWN =
static constexpr int32_t POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// An arbitrary injector pid / uid pair that has permission to inject events.
-static const int32_t INJECTOR_PID = 999;
-static const int32_t INJECTOR_UID = 1001;
+// The default pid and uid for windows created by the test.
+static constexpr int32_t WINDOW_PID = 999;
+static constexpr int32_t WINDOW_UID = 1001;
+
+// The default policy flags to use for event injection by tests.
+static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
// An arbitrary pid of the gesture monitor window
static constexpr int32_t MONITOR_PID = 2001;
@@ -470,10 +473,6 @@ private:
void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
- bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override {
- return pid == INJECTOR_PID && uid == INJECTOR_UID;
- }
-
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
@@ -558,8 +557,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
/*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
@@ -567,8 +566,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -597,8 +596,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
@@ -609,8 +608,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -621,8 +620,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
@@ -633,8 +632,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -645,8 +644,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
@@ -657,8 +656,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -668,8 +667,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
@@ -681,8 +680,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
@@ -693,8 +692,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -707,8 +706,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- InputEventInjectionSync::NONE, 0ms, 0))
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+ 0ms, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
@@ -1011,8 +1010,8 @@ public:
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
- mInfo.ownerPid = INJECTOR_PID;
- mInfo.ownerUid = INJECTOR_UID;
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
mInfo.displayId = displayId;
mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
}
@@ -1294,7 +1293,8 @@ static InputEventInjectionResult injectKey(
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- bool allowKeyRepeat = true) {
+ bool allowKeyRepeat = true, std::optional<int32_t> targetUid = {},
+ uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1303,13 +1303,11 @@ static InputEventInjectionResult injectKey(
INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
repeatCount, currentTime, currentTime);
- int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
if (!allowKeyRepeat) {
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
- injectionTimeout, policyFlags);
+ return dispatcher->injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
}
static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
@@ -1452,10 +1450,10 @@ private:
static InputEventInjectionResult injectMotionEvent(
const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
- return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
- injectionTimeout,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+ std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+ return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
+ policyFlags);
}
static InputEventInjectionResult injectMotionEvent(
@@ -1465,7 +1463,8 @@ static InputEventInjectionResult injectMotionEvent(
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
- nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
+ std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
MotionEvent event = MotionEventBuilder(action, source)
.displayId(displayId)
.eventTime(eventTime)
@@ -1477,7 +1476,8 @@ static InputEventInjectionResult injectMotionEvent(
.build();
// Inject event until dispatch out.
- return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
+ return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode, targetUid,
+ policyFlags);
}
static InputEventInjectionResult injectMotionDown(
@@ -3466,8 +3466,8 @@ TEST_F(InputDispatcherTest, DisplayRemoved) {
* FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
*/
TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
- constexpr int32_t SLIPPERY_PID = INJECTOR_PID + 1;
- constexpr int32_t SLIPPERY_UID = INJECTOR_UID + 1;
+ constexpr int32_t SLIPPERY_PID = WINDOW_PID + 1;
+ constexpr int32_t SLIPPERY_UID = WINDOW_UID + 1;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -3994,7 +3994,7 @@ protected:
const int32_t additionalPolicyFlags =
POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/,
InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
policyFlags | additionalPolicyFlags));
@@ -4029,7 +4029,7 @@ protected:
const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ mDispatcher->injectInputEvent(&event, {} /*targetUid*/,
InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
policyFlags | additionalPolicyFlags));
@@ -4536,7 +4536,7 @@ TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
InputEventInjectionResult result =
- mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ mDispatcher->injectInputEvent(&event, {} /* targetUid */,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, policyFlags);
ASSERT_EQ(InputEventInjectionResult::FAILED, result)
@@ -6336,8 +6336,8 @@ protected:
mWindow->consumeFocusEvent(true);
// Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
- if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, INJECTOR_PID,
- INJECTOR_UID, /* hasPermission */ true)) {
+ if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
+ WINDOW_UID, /* hasPermission */ true)) {
mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
}
@@ -6970,4 +6970,149 @@ TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) {
window->assertNoEvents();
}
+struct User {
+ int32_t mPid;
+ int32_t mUid;
+ uint32_t mPolicyFlags{DEFAULT_POLICY_FLAGS};
+ std::unique_ptr<InputDispatcher>& mDispatcher;
+
+ User(std::unique_ptr<InputDispatcher>& dispatcher, int32_t pid, int32_t uid)
+ : mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
+
+ InputEventInjectionResult injectTargetedMotion(int32_t action) const {
+ return injectMotionEvent(mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {100, 200},
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT,
+ systemTime(SYSTEM_TIME_MONOTONIC), {mUid}, mPolicyFlags);
+ }
+
+ InputEventInjectionResult injectTargetedKey(int32_t action) const {
+ return inputdispatcher::injectKey(mDispatcher, action, 0 /* repeatCount*/, ADISPLAY_ID_NONE,
+ InputEventInjectionSync::WAIT_FOR_RESULT,
+ INJECT_EVENT_TIMEOUT, false /*allowKeyRepeat*/, {mUid},
+ mPolicyFlags);
+ }
+
+ sp<FakeWindowHandle> createWindow() const {
+ std::shared_ptr<FakeApplicationHandle> overlayApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(overlayApplication, mDispatcher,
+ "Owned Window", ADISPLAY_ID_DEFAULT);
+ window->setOwnerInfo(mPid, mUid);
+ return window;
+ }
+};
+
+using InputDispatcherTargetedInjectionTest = InputDispatcherTest;
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionDown();
+
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ owner.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
+ window->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ auto rando = User(mDispatcher, 20, 21);
+ EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
+ rando.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
+ rando.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedSpyWindow) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+ auto spy = owner.createWindow();
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+ spy->consumeMotionDown();
+ window->consumeMotionDown();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedSpyWindow) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+
+ auto rando = User(mDispatcher, 20, 21);
+ auto randosSpy = rando.createWindow();
+ randosSpy->setSpy(true);
+ randosSpy->setTrustedOverlay(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+
+ // The event is targeted at owner's window, so injection should succeed, but the spy should
+ // not receive the event.
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+ randosSpy->assertNoEvents();
+ window->consumeMotionDown();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTargeting) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+
+ auto rando = User(mDispatcher, 20, 21);
+ auto randosSpy = rando.createWindow();
+ randosSpy->setSpy(true);
+ randosSpy->setTrustedOverlay(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+
+ // A user that has injection permission can inject into any window.
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
+ randosSpy->consumeMotionDown();
+ window->consumeMotionDown();
+
+ setFocusedWindow(randosSpy);
+ randosSpy->consumeFocusEvent(true);
+
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanGenerateActionOutsideToOtherUids) {
+ auto owner = User(mDispatcher, 10, 11);
+ auto window = owner.createWindow();
+
+ auto rando = User(mDispatcher, 20, 21);
+ auto randosWindow = rando.createWindow();
+ randosWindow->setFrame(Rect{-10, -10, -5, -5});
+ randosWindow->setWatchOutsideTouch(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosWindow, window}}});
+
+ // We allow generation of ACTION_OUTSIDE events into windows owned by different uids.
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+ owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionDown();
+ randosWindow->consumeMotionOutside();
+}
+
} // namespace android::inputdispatcher