diff options
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 7852b30875..b5a3825fc3 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; @@ -1720,8 +1745,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) { @@ -1972,9 +1996,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( @@ -2040,11 +2065,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. @@ -2054,7 +2074,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; @@ -2103,7 +2122,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; @@ -2136,6 +2155,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()) { @@ -2179,6 +2206,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()); @@ -2272,6 +2304,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)) { @@ -2363,19 +2403,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. @@ -2441,19 +2488,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) { @@ -2685,26 +2719,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 @@ -4223,20 +4237,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 @@ -4377,7 +4391,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; } @@ -4449,8 +4463,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; @@ -4489,19 +4502,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)) { @@ -4510,12 +4516,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 34aed3bb74..ed89ed0b0f 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; @@ -279,7 +279,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&, @@ -555,8 +554,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 d7bc5fbfe9..67fed8b4f1 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 61e5fe356d..91a666c699 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; @@ -61,9 +61,12 @@ static constexpr int32_t POINTER_2_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; @@ -472,10 +475,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; @@ -560,8 +559,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. @@ -569,8 +568,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."; } @@ -599,8 +598,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. @@ -611,8 +610,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, @@ -623,8 +622,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. @@ -635,8 +634,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, @@ -647,8 +646,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. @@ -659,8 +658,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, @@ -670,8 +669,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. @@ -683,8 +682,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; @@ -695,8 +694,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. @@ -709,8 +708,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."; } @@ -1013,8 +1012,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; } @@ -1296,7 +1295,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); @@ -1305,13 +1305,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, @@ -1454,10 +1452,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( @@ -1467,7 +1465,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) @@ -1479,7 +1478,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( @@ -3574,8 +3574,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); @@ -4102,7 +4102,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)); @@ -4137,7 +4137,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)); @@ -4644,7 +4644,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) @@ -6558,8 +6558,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); } @@ -7190,4 +7190,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 |