Loading libs/input/android/os/InputEventInjectionResult.aidl +2 −3 Original line number Original line Diff line number Diff line Loading @@ -29,9 +29,8 @@ enum InputEventInjectionResult { /* Injection succeeded. */ /* Injection succeeded. */ SUCCEEDED = 0, SUCCEEDED = 0, /* Injection failed because the injector did not have permission to inject /* Injection failed because the injected event did not target the appropriate window. */ * into the application with input focus. */ TARGET_MISMATCH = 1, PERMISSION_DENIED = 1, /* Injection failed because there were no available input targets. */ /* Injection failed because there were no available input targets. */ FAILED = 2, FAILED = 2, Loading services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +10 −12 Original line number Original line Diff line number Diff line Loading @@ -31,11 +31,11 @@ using android::os::InputEventInjectionSync; namespace android::inputdispatcher { namespace android::inputdispatcher { // An arbitrary device id. // 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. // The default pid and uid for windows created by the test. static const int32_t INJECTOR_PID = 999; constexpr int32_t WINDOW_PID = 999; static const int32_t INJECTOR_UID = 1001; constexpr int32_t WINDOW_UID = 1001; static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s; static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; Loading Loading @@ -108,8 +108,6 @@ private: void pokeUserActivity(nsecs_t, int32_t, int32_t) override {} 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 onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} void setPointerCapture(const PointerCaptureRequest&) override {} void setPointerCapture(const PointerCaptureRequest&) override {} Loading Loading @@ -196,8 +194,8 @@ public: mInfo.globalScaleFactor = 1.0; mInfo.globalScaleFactor = 1.0; mInfo.touchableRegion.clear(); mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.addTouchableRegion(mFrame); mInfo.ownerPid = INJECTOR_PID; mInfo.ownerPid = WINDOW_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.ownerUid = WINDOW_UID; mInfo.displayId = ADISPLAY_ID_DEFAULT; mInfo.displayId = ADISPLAY_ID_DEFAULT; } } Loading Loading @@ -310,14 +308,14 @@ static void benchmarkInjectMotion(benchmark::State& state) { for (auto _ : state) { for (auto _ : state) { MotionEvent event = generateMotionEvent(); MotionEvent event = generateMotionEvent(); // Send ACTION_DOWN // Send ACTION_DOWN dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); // Send ACTION_UP // Send ACTION_UP event.setAction(AMOTION_EVENT_ACTION_UP); event.setAction(AMOTION_EVENT_ACTION_UP); dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); window->consumeEvent(); window->consumeEvent(); Loading services/inputflinger/dispatcher/InjectionState.cpp +2 −3 Original line number Original line Diff line number Diff line Loading @@ -20,10 +20,9 @@ namespace android::inputdispatcher { namespace android::inputdispatcher { InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) InjectionState::InjectionState(const std::optional<int32_t>& targetUid) : refCount(1), : refCount(1), injectorPid(injectorPid), targetUid(targetUid), injectorUid(injectorUid), injectionResult(android::os::InputEventInjectionResult::PENDING), injectionResult(android::os::InputEventInjectionResult::PENDING), injectionIsAsync(false), injectionIsAsync(false), pendingForegroundDispatches(0) {} pendingForegroundDispatches(0) {} Loading services/inputflinger/dispatcher/InjectionState.h +2 −3 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,12 @@ namespace inputdispatcher { struct InjectionState { struct InjectionState { mutable int32_t refCount; mutable int32_t refCount; int32_t injectorPid; std::optional<int32_t> targetUid; int32_t injectorUid; android::os::InputEventInjectionResult injectionResult; // initially PENDING android::os::InputEventInjectionResult injectionResult; // initially PENDING bool injectionIsAsync; // set to true if injection is not waiting for the result bool injectionIsAsync; // set to true if injection is not waiting for the result int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress 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(); void release(); private: private: Loading services/inputflinger/dispatcher/InputDispatcher.cpp +87 −81 Original line number Original line Diff line number Diff line Loading @@ -578,6 +578,27 @@ bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int3 return false; 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 } // namespace // --- InputDispatcher --- // --- InputDispatcher --- Loading Loading @@ -1036,6 +1057,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE switch (entry.type) { switch (entry.type) { case EventEntry::Type::KEY: { case EventEntry::Type::KEY: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); // Optimize app switch latency. // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // If the application takes too long to catch up then we drop all events preceding // the app switch key. // the app switch key. Loading Loading @@ -1073,6 +1096,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE } } case EventEntry::Type::MOTION: { case EventEntry::Type::MOTION: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { mNextUnblockedEvent = mInboundQueue.back(); mNextUnblockedEvent = mInboundQueue.back(); needWake = true; needWake = true; Loading Loading @@ -1718,8 +1743,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< } } setInjectionResult(*entry, injectionResult); setInjectionResult(*entry, injectionResult); if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) { if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) { ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent)); return true; return true; } } if (injectionResult != InputEventInjectionResult::SUCCEEDED) { if (injectionResult != InputEventInjectionResult::SUCCEEDED) { Loading Loading @@ -1976,9 +2000,10 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( // we have a valid, non-null focused window // we have a valid, non-null focused window resetNoFocusedWindowTimeoutLocked(); resetNoFocusedWindowTimeoutLocked(); // Check permissions. // Verify targeted injection. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) { return InputEventInjectionResult::PERMISSION_DENIED; ALOGW("Dropping injected event: %s", (*err).c_str()); return InputEventInjectionResult::TARGET_MISMATCH; } } if (focusedWindowHandle->getInfo()->inputConfig.test( if (focusedWindowHandle->getInfo()->inputConfig.test( Loading Loading @@ -2044,11 +2069,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ATRACE_CALL(); 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 // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. // event injection will be allowed. Loading @@ -2058,7 +2078,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the touch state as needed based on the properties of the touch event. // Update the touch state as needed based on the properties of the touch event. InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); sp<WindowInfoHandle> newTouchedWindowHandle; sp<WindowInfoHandle> newTouchedWindowHandle; Loading Loading @@ -2107,7 +2126,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( "in display %" PRId32, "in display %" PRId32, displayId); displayId); // TODO: test multiple simultaneous input streams. // TODO: test multiple simultaneous input streams. injectionResult = InputEventInjectionResult::PERMISSION_DENIED; injectionResult = InputEventInjectionResult::FAILED; switchedDevice = false; switchedDevice = false; wrongDevice = true; wrongDevice = true; goto Failed; goto Failed; Loading Loading @@ -2140,6 +2159,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); 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. // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr) { if (newTouchedWindowHandle != nullptr) { if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { Loading Loading @@ -2183,6 +2210,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { const WindowInfo& info = *windowHandle->getInfo(); 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)) { if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) { ALOGI("Not sending touch event to %s because it is paused", ALOGI("Not sending touch event to %s because it is paused", windowHandle->getName().c_str()); windowHandle->getName().c_str()); Loading Loading @@ -2276,6 +2308,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindowHandle = newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus); 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 // Drop touch events if requested by input feature if (newTouchedWindowHandle != nullptr && if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) { shouldDropInput(entry, newTouchedWindowHandle)) { Loading Loading @@ -2367,19 +2407,26 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( goto Failed; goto Failed; } } // Check permission to inject into all touched foreground windows. // Ensure that all touched windows are valid for injection. if (std::any_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), if (entry.injectionState != nullptr) { [this, &entry](const TouchedWindow& touchedWindow) { std::string errs; return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 && for (const TouchedWindow& touchedWindow : tempTouchState.windows) { !checkInjectionPermission(touchedWindow.windowHandle, if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { entry.injectionState); // Allow ACTION_OUTSIDE events generated by targeted injection to be })) { // dispatched to any uid, since the coords will be zeroed out later. injectionResult = InputEventInjectionResult::PERMISSION_DENIED; continue; injectionPermission = INJECTION_PERMISSION_DENIED; } 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; 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 // 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. // set the policy flag that we will not reveal coordinate information to this window. Loading Loading @@ -2445,19 +2492,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState.filterNonAsIsTouchWindows(); tempTouchState.filterNonAsIsTouchWindows(); Failed: 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. // Update final pieces of touch state if the injector had permission. if (!wrongDevice) { if (!wrongDevice) { if (switchedDevice) { if (switchedDevice) { Loading Loading @@ -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 * Indicate whether one window handle should be considered as obscuring * another window handle. We only check a few preconditions. Actually * another window handle. We only check a few preconditions. Actually Loading Loading @@ -4193,20 +4207,20 @@ void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChan } } } } InputEventInjectionResult InputDispatcher::injectInputEvent( InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event, const InputEvent* event, int32_t injectorPid, int32_t injectorUid, std::optional<int32_t> targetUid, InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { if (DEBUG_INBOUND_EVENT_DETAILS) { if (DEBUG_INBOUND_EVENT_DETAILS) { ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, " "syncMode=%d, timeout=%lld, policyFlags=0x%08x", "policyFlags=0x%08x", event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); 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(); nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count(); policyFlags |= POLICY_FLAG_INJECTED; policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED; if (hasInjectionPermission(injectorPid, injectorUid)) { policyFlags |= POLICY_FLAG_TRUSTED; } // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events // 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 // that have gone through the InputFilter. If the event passed through the InputFilter, assign Loading Loading @@ -4347,7 +4361,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( return InputEventInjectionResult::FAILED; return InputEventInjectionResult::FAILED; } } InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); InjectionState* injectionState = new InjectionState(targetUid); if (syncMode == InputEventInjectionSync::NONE) { if (syncMode == InputEventInjectionSync::NONE) { injectionState->injectionIsAsync = true; injectionState->injectionIsAsync = true; } } Loading Loading @@ -4419,8 +4433,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( } // release lock } // release lock if (DEBUG_INJECTION) { if (DEBUG_INJECTION) { ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d", ALOGD("injectInputEvent - Finished with result %d.", injectionResult); injectionResult, injectorPid, injectorUid); } } return injectionResult; return injectionResult; Loading Loading @@ -4459,19 +4472,12 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu return result; return result; } } bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { return injectorUid == 0 || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); } void InputDispatcher::setInjectionResult(EventEntry& entry, void InputDispatcher::setInjectionResult(EventEntry& entry, InputEventInjectionResult injectionResult) { InputEventInjectionResult injectionResult) { InjectionState* injectionState = entry.injectionState; InjectionState* injectionState = entry.injectionState; if (injectionState) { if (injectionState) { if (DEBUG_INJECTION) { if (DEBUG_INJECTION) { ALOGD("Setting input event injection result to %d. " ALOGD("Setting input event injection result to %d.", injectionResult); "injectorPid=%d, injectorUid=%d", injectionResult, injectionState->injectorPid, injectionState->injectorUid); } } if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { Loading @@ -4480,12 +4486,12 @@ void InputDispatcher::setInjectionResult(EventEntry& entry, case InputEventInjectionResult::SUCCEEDED: case InputEventInjectionResult::SUCCEEDED: ALOGV("Asynchronous input event injection succeeded."); ALOGV("Asynchronous input event injection succeeded."); break; break; case InputEventInjectionResult::TARGET_MISMATCH: ALOGV("Asynchronous input event injection target mismatch."); break; case InputEventInjectionResult::FAILED: case InputEventInjectionResult::FAILED: ALOGW("Asynchronous input event injection failed."); ALOGW("Asynchronous input event injection failed."); break; break; case InputEventInjectionResult::PERMISSION_DENIED: ALOGW("Asynchronous input event injection permission denied."); break; case InputEventInjectionResult::TIMED_OUT: case InputEventInjectionResult::TIMED_OUT: ALOGW("Asynchronous input event injection timed out."); ALOGW("Asynchronous input event injection timed out."); break; break; Loading Loading
libs/input/android/os/InputEventInjectionResult.aidl +2 −3 Original line number Original line Diff line number Diff line Loading @@ -29,9 +29,8 @@ enum InputEventInjectionResult { /* Injection succeeded. */ /* Injection succeeded. */ SUCCEEDED = 0, SUCCEEDED = 0, /* Injection failed because the injector did not have permission to inject /* Injection failed because the injected event did not target the appropriate window. */ * into the application with input focus. */ TARGET_MISMATCH = 1, PERMISSION_DENIED = 1, /* Injection failed because there were no available input targets. */ /* Injection failed because there were no available input targets. */ FAILED = 2, FAILED = 2, Loading
services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +10 −12 Original line number Original line Diff line number Diff line Loading @@ -31,11 +31,11 @@ using android::os::InputEventInjectionSync; namespace android::inputdispatcher { namespace android::inputdispatcher { // An arbitrary device id. // 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. // The default pid and uid for windows created by the test. static const int32_t INJECTOR_PID = 999; constexpr int32_t WINDOW_PID = 999; static const int32_t INJECTOR_UID = 1001; constexpr int32_t WINDOW_UID = 1001; static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s; static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; Loading Loading @@ -108,8 +108,6 @@ private: void pokeUserActivity(nsecs_t, int32_t, int32_t) override {} 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 onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} void setPointerCapture(const PointerCaptureRequest&) override {} void setPointerCapture(const PointerCaptureRequest&) override {} Loading Loading @@ -196,8 +194,8 @@ public: mInfo.globalScaleFactor = 1.0; mInfo.globalScaleFactor = 1.0; mInfo.touchableRegion.clear(); mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.addTouchableRegion(mFrame); mInfo.ownerPid = INJECTOR_PID; mInfo.ownerPid = WINDOW_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.ownerUid = WINDOW_UID; mInfo.displayId = ADISPLAY_ID_DEFAULT; mInfo.displayId = ADISPLAY_ID_DEFAULT; } } Loading Loading @@ -310,14 +308,14 @@ static void benchmarkInjectMotion(benchmark::State& state) { for (auto _ : state) { for (auto _ : state) { MotionEvent event = generateMotionEvent(); MotionEvent event = generateMotionEvent(); // Send ACTION_DOWN // Send ACTION_DOWN dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); // Send ACTION_UP // Send ACTION_UP event.setAction(AMOTION_EVENT_ACTION_UP); event.setAction(AMOTION_EVENT_ACTION_UP); dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); window->consumeEvent(); window->consumeEvent(); Loading
services/inputflinger/dispatcher/InjectionState.cpp +2 −3 Original line number Original line Diff line number Diff line Loading @@ -20,10 +20,9 @@ namespace android::inputdispatcher { namespace android::inputdispatcher { InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) InjectionState::InjectionState(const std::optional<int32_t>& targetUid) : refCount(1), : refCount(1), injectorPid(injectorPid), targetUid(targetUid), injectorUid(injectorUid), injectionResult(android::os::InputEventInjectionResult::PENDING), injectionResult(android::os::InputEventInjectionResult::PENDING), injectionIsAsync(false), injectionIsAsync(false), pendingForegroundDispatches(0) {} pendingForegroundDispatches(0) {} Loading
services/inputflinger/dispatcher/InjectionState.h +2 −3 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,12 @@ namespace inputdispatcher { struct InjectionState { struct InjectionState { mutable int32_t refCount; mutable int32_t refCount; int32_t injectorPid; std::optional<int32_t> targetUid; int32_t injectorUid; android::os::InputEventInjectionResult injectionResult; // initially PENDING android::os::InputEventInjectionResult injectionResult; // initially PENDING bool injectionIsAsync; // set to true if injection is not waiting for the result bool injectionIsAsync; // set to true if injection is not waiting for the result int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress 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(); void release(); private: private: Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +87 −81 Original line number Original line Diff line number Diff line Loading @@ -578,6 +578,27 @@ bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int3 return false; 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 } // namespace // --- InputDispatcher --- // --- InputDispatcher --- Loading Loading @@ -1036,6 +1057,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE switch (entry.type) { switch (entry.type) { case EventEntry::Type::KEY: { case EventEntry::Type::KEY: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); // Optimize app switch latency. // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // If the application takes too long to catch up then we drop all events preceding // the app switch key. // the app switch key. Loading Loading @@ -1073,6 +1096,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE } } case EventEntry::Type::MOTION: { case EventEntry::Type::MOTION: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { mNextUnblockedEvent = mInboundQueue.back(); mNextUnblockedEvent = mInboundQueue.back(); needWake = true; needWake = true; Loading Loading @@ -1718,8 +1743,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< } } setInjectionResult(*entry, injectionResult); setInjectionResult(*entry, injectionResult); if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) { if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) { ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent)); return true; return true; } } if (injectionResult != InputEventInjectionResult::SUCCEEDED) { if (injectionResult != InputEventInjectionResult::SUCCEEDED) { Loading Loading @@ -1976,9 +2000,10 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( // we have a valid, non-null focused window // we have a valid, non-null focused window resetNoFocusedWindowTimeoutLocked(); resetNoFocusedWindowTimeoutLocked(); // Check permissions. // Verify targeted injection. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) { return InputEventInjectionResult::PERMISSION_DENIED; ALOGW("Dropping injected event: %s", (*err).c_str()); return InputEventInjectionResult::TARGET_MISMATCH; } } if (focusedWindowHandle->getInfo()->inputConfig.test( if (focusedWindowHandle->getInfo()->inputConfig.test( Loading Loading @@ -2044,11 +2069,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ATRACE_CALL(); 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 // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. // event injection will be allowed. Loading @@ -2058,7 +2078,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the touch state as needed based on the properties of the touch event. // Update the touch state as needed based on the properties of the touch event. InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle); sp<WindowInfoHandle> newTouchedWindowHandle; sp<WindowInfoHandle> newTouchedWindowHandle; Loading Loading @@ -2107,7 +2126,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( "in display %" PRId32, "in display %" PRId32, displayId); displayId); // TODO: test multiple simultaneous input streams. // TODO: test multiple simultaneous input streams. injectionResult = InputEventInjectionResult::PERMISSION_DENIED; injectionResult = InputEventInjectionResult::FAILED; switchedDevice = false; switchedDevice = false; wrongDevice = true; wrongDevice = true; goto Failed; goto Failed; Loading Loading @@ -2140,6 +2159,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); 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. // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr) { if (newTouchedWindowHandle != nullptr) { if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { Loading Loading @@ -2183,6 +2210,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { const WindowInfo& info = *windowHandle->getInfo(); 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)) { if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) { ALOGI("Not sending touch event to %s because it is paused", ALOGI("Not sending touch event to %s because it is paused", windowHandle->getName().c_str()); windowHandle->getName().c_str()); Loading Loading @@ -2276,6 +2308,14 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindowHandle = newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus); 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 // Drop touch events if requested by input feature if (newTouchedWindowHandle != nullptr && if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) { shouldDropInput(entry, newTouchedWindowHandle)) { Loading Loading @@ -2367,19 +2407,26 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( goto Failed; goto Failed; } } // Check permission to inject into all touched foreground windows. // Ensure that all touched windows are valid for injection. if (std::any_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), if (entry.injectionState != nullptr) { [this, &entry](const TouchedWindow& touchedWindow) { std::string errs; return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 && for (const TouchedWindow& touchedWindow : tempTouchState.windows) { !checkInjectionPermission(touchedWindow.windowHandle, if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { entry.injectionState); // Allow ACTION_OUTSIDE events generated by targeted injection to be })) { // dispatched to any uid, since the coords will be zeroed out later. injectionResult = InputEventInjectionResult::PERMISSION_DENIED; continue; injectionPermission = INJECTION_PERMISSION_DENIED; } 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; 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 // 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. // set the policy flag that we will not reveal coordinate information to this window. Loading Loading @@ -2445,19 +2492,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState.filterNonAsIsTouchWindows(); tempTouchState.filterNonAsIsTouchWindows(); Failed: 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. // Update final pieces of touch state if the injector had permission. if (!wrongDevice) { if (!wrongDevice) { if (switchedDevice) { if (switchedDevice) { Loading Loading @@ -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 * Indicate whether one window handle should be considered as obscuring * another window handle. We only check a few preconditions. Actually * another window handle. We only check a few preconditions. Actually Loading Loading @@ -4193,20 +4207,20 @@ void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChan } } } } InputEventInjectionResult InputDispatcher::injectInputEvent( InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event, const InputEvent* event, int32_t injectorPid, int32_t injectorUid, std::optional<int32_t> targetUid, InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { if (DEBUG_INBOUND_EVENT_DETAILS) { if (DEBUG_INBOUND_EVENT_DETAILS) { ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, " "syncMode=%d, timeout=%lld, policyFlags=0x%08x", "policyFlags=0x%08x", event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); 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(); nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count(); policyFlags |= POLICY_FLAG_INJECTED; policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED; if (hasInjectionPermission(injectorPid, injectorUid)) { policyFlags |= POLICY_FLAG_TRUSTED; } // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events // 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 // that have gone through the InputFilter. If the event passed through the InputFilter, assign Loading Loading @@ -4347,7 +4361,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( return InputEventInjectionResult::FAILED; return InputEventInjectionResult::FAILED; } } InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); InjectionState* injectionState = new InjectionState(targetUid); if (syncMode == InputEventInjectionSync::NONE) { if (syncMode == InputEventInjectionSync::NONE) { injectionState->injectionIsAsync = true; injectionState->injectionIsAsync = true; } } Loading Loading @@ -4419,8 +4433,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( } // release lock } // release lock if (DEBUG_INJECTION) { if (DEBUG_INJECTION) { ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d", ALOGD("injectInputEvent - Finished with result %d.", injectionResult); injectionResult, injectorPid, injectorUid); } } return injectionResult; return injectionResult; Loading Loading @@ -4459,19 +4472,12 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu return result; return result; } } bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { return injectorUid == 0 || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); } void InputDispatcher::setInjectionResult(EventEntry& entry, void InputDispatcher::setInjectionResult(EventEntry& entry, InputEventInjectionResult injectionResult) { InputEventInjectionResult injectionResult) { InjectionState* injectionState = entry.injectionState; InjectionState* injectionState = entry.injectionState; if (injectionState) { if (injectionState) { if (DEBUG_INJECTION) { if (DEBUG_INJECTION) { ALOGD("Setting input event injection result to %d. " ALOGD("Setting input event injection result to %d.", injectionResult); "injectorPid=%d, injectorUid=%d", injectionResult, injectionState->injectorPid, injectionState->injectorUid); } } if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { Loading @@ -4480,12 +4486,12 @@ void InputDispatcher::setInjectionResult(EventEntry& entry, case InputEventInjectionResult::SUCCEEDED: case InputEventInjectionResult::SUCCEEDED: ALOGV("Asynchronous input event injection succeeded."); ALOGV("Asynchronous input event injection succeeded."); break; break; case InputEventInjectionResult::TARGET_MISMATCH: ALOGV("Asynchronous input event injection target mismatch."); break; case InputEventInjectionResult::FAILED: case InputEventInjectionResult::FAILED: ALOGW("Asynchronous input event injection failed."); ALOGW("Asynchronous input event injection failed."); break; break; case InputEventInjectionResult::PERMISSION_DENIED: ALOGW("Asynchronous input event injection permission denied."); break; case InputEventInjectionResult::TIMED_OUT: case InputEventInjectionResult::TIMED_OUT: ALOGW("Asynchronous input event injection timed out."); ALOGW("Asynchronous input event injection timed out."); break; break; Loading