| /* |
| * Copyright 2024 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "../InputCommonConverter.h" |
| #include "../dispatcher/InputDispatcher.h" |
| #include "../dispatcher/trace/InputTracingPerfettoBackend.h" |
| #include "../dispatcher/trace/ThreadedBackend.h" |
| #include "FakeApplicationHandle.h" |
| #include "FakeInputDispatcherPolicy.h" |
| #include "FakeWindows.h" |
| #include "InputTraceSession.h" |
| #include "TestEventMatchers.h" |
| |
| #include <NotifyArgsBuilders.h> |
| #include <android-base/logging.h> |
| #include <android/content/pm/IPackageManagerNative.h> |
| #include <gtest/gtest.h> |
| #include <input/Input.h> |
| #include <perfetto/trace/android/android_input_event.pbzero.h> |
| #include <perfetto/trace/android/winscope_extensions.pbzero.h> |
| #include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> |
| #include <perfetto/trace/trace.pbzero.h> |
| #include <private/android_filesystem_config.h> |
| #include <map> |
| #include <vector> |
| |
| namespace android::inputdispatcher::trace { |
| |
| using perfetto::protos::pbzero::AndroidInputEventConfig; |
| |
| namespace { |
| |
| constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; |
| |
| // Ensure common actions are interchangeable between keys and motions for convenience. |
| static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_DOWN) == |
| static_cast<int32_t>(AKEY_EVENT_ACTION_DOWN)); |
| static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_UP) == |
| static_cast<int32_t>(AKEY_EVENT_ACTION_UP)); |
| constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; |
| constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; |
| constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP; |
| constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL; |
| |
| constexpr gui::Pid PID{1}; |
| |
| constexpr gui::Uid ALLOWED_UID_1{10012}; |
| constexpr gui::Uid ALLOWED_UID_2{10013}; |
| constexpr gui::Uid DISALLOWED_UID_1{1}; |
| constexpr gui::Uid DISALLOWED_UID_2{99}; |
| constexpr gui::Uid UNLISTED_UID{12345}; |
| |
| const std::string ALLOWED_PKG_1{"allowed.pkg.1"}; |
| const std::string ALLOWED_PKG_2{"allowed.pkg.2"}; |
| const std::string DISALLOWED_PKG_1{"disallowed.pkg.1"}; |
| const std::string DISALLOWED_PKG_2{"disallowed.pkg.2"}; |
| |
| const std::map<std::string, gui::Uid> kPackageUidMap{ |
| {ALLOWED_PKG_1, ALLOWED_UID_1}, |
| {ALLOWED_PKG_2, ALLOWED_UID_2}, |
| {DISALLOWED_PKG_1, DISALLOWED_UID_1}, |
| {DISALLOWED_PKG_2, DISALLOWED_UID_2}, |
| }; |
| |
| class FakePackageManager : public content::pm::IPackageManagerNativeDefault { |
| public: |
| binder::Status getPackageUid(const ::std::string& pkg, int64_t flags, int32_t userId, |
| int32_t* outUid) override { |
| auto it = kPackageUidMap.find(pkg); |
| *outUid = it != kPackageUidMap.end() ? static_cast<int32_t>(it->second.val()) : -1; |
| return binder::Status::ok(); |
| } |
| }; |
| |
| const sp<testing::NiceMock<FakePackageManager>> kPackageManager = |
| sp<testing::NiceMock<FakePackageManager>>::make(); |
| |
| const std::shared_ptr<FakeApplicationHandle> APP = std::make_shared<FakeApplicationHandle>(); |
| |
| } // namespace |
| |
| // --- InputTracingTest --- |
| |
| class InputTracingTest : public testing::Test { |
| protected: |
| std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy; |
| std::unique_ptr<InputDispatcher> mDispatcher; |
| |
| void SetUp() override { |
| impl::PerfettoBackend::sUseInProcessBackendForTest = true; |
| impl::PerfettoBackend::sPackageManagerProvider = []() { return kPackageManager; }; |
| mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); |
| |
| auto tracingBackend = std::make_unique<impl::ThreadedBackend<impl::PerfettoBackend>>( |
| impl::PerfettoBackend()); |
| mRequestTracerIdle = tracingBackend->getIdleWaiterForTesting(); |
| mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, std::move(tracingBackend)); |
| |
| mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); |
| ASSERT_EQ(OK, mDispatcher->start()); |
| } |
| |
| void TearDown() override { |
| ASSERT_EQ(OK, mDispatcher->stop()); |
| mDispatcher.reset(); |
| mFakePolicy.reset(); |
| } |
| |
| void waitForTracerIdle() { |
| mDispatcher->waitForIdle(); |
| mRequestTracerIdle(); |
| } |
| |
| void setFocusedWindow(const sp<gui::WindowInfoHandle>& window) { |
| gui::FocusRequest request; |
| request.token = window->getToken(); |
| request.windowName = window->getName(); |
| request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); |
| request.displayId = window->getInfo()->displayId.val(); |
| mDispatcher->setFocusedWindow(request); |
| } |
| |
| void tapAndExpect(const std::vector<sp<FakeWindowHandle>>& windows, Level inboundTraceLevel, |
| Level dispatchTraceLevel, InputTraceSession& s) { |
| const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(down); |
| s.expectMotionTraced(inboundTraceLevel, toMotionEvent(down)); |
| for (const auto& window : windows) { |
| auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); |
| s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); |
| } |
| |
| const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(up); |
| s.expectMotionTraced(inboundTraceLevel, toMotionEvent(up)); |
| for (const auto& window : windows) { |
| auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP)); |
| s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); |
| } |
| } |
| |
| void keypressAndExpect(const std::vector<sp<FakeWindowHandle>>& windows, |
| Level inboundTraceLevel, Level dispatchTraceLevel, |
| InputTraceSession& s) { |
| const auto down = KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(); |
| mDispatcher->notifyKey(down); |
| s.expectKeyTraced(inboundTraceLevel, toKeyEvent(down)); |
| for (const auto& window : windows) { |
| auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_DOWN)); |
| s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); |
| } |
| |
| const auto up = KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build(); |
| mDispatcher->notifyKey(up); |
| s.expectKeyTraced(inboundTraceLevel, toKeyEvent(up)); |
| for (const auto& window : windows) { |
| auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_UP)); |
| s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); |
| } |
| } |
| |
| private: |
| std::function<void()> mRequestTracerIdle; |
| }; |
| |
| TEST_F(InputTracingTest, EmptyConfigTracesNothing) { |
| InputTraceSession s{[](auto& config) {}}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| keypressAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, TraceAll) { |
| InputTraceSession s{ |
| [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, NoRulesTracesNothing) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| keypressAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, EmptyRuleMatchesEverything) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match everything as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, UnspecifiedTracelLevel) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match everything, trace level unspecified |
| auto rule = config->add_rules(); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| // Event is not traced by default if trace level is unspecified |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| keypressAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MatchSecureWindow) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match secure windows as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->set_match_secure(true); |
| }}; |
| |
| // Add a normal window and a spy window. |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| // Since neither are secure windows, events should not be traced. |
| tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); |
| |
| // Events should be matched as secure if any of the target windows is marked as secure. |
| spy->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| spy->setSecure(false); |
| window->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| spy->setSecure(true); |
| window->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| spy->setSecure(false); |
| window->setSecure(false); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MatchImeConnectionActive) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match IME Connection Active as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->set_match_ime_connection_active(true); |
| }}; |
| |
| // Add a normal window and a spy window. |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| // Since IME connection is not active, events should not be traced. |
| tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); |
| |
| mDispatcher->setInputMethodConnectionIsActive(true); |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| mDispatcher->setInputMethodConnectionIsActive(false); |
| tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MatchAllPackages) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match all package as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->add_match_all_packages(ALLOWED_PKG_1); |
| rule->add_match_all_packages(ALLOWED_PKG_2); |
| }}; |
| |
| // All windows are allowlisted. |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, ALLOWED_UID_1); |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setOwnerInfo(PID, ALLOWED_UID_2); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| auto systemSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| systemSpy->setOwnerInfo(PID, gui::Uid{AID_SYSTEM}); |
| systemSpy->setSpy(true); |
| systemSpy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged( |
| {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| // Add a disallowed spy. This will result in the event not being traced for all windows. |
| auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1); |
| disallowedSpy->setSpy(true); |
| disallowedSpy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(), |
| *disallowedSpy->getInfo(), *window->getInfo()}, |
| {}, |
| 0, |
| 0}); |
| |
| tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s); |
| |
| // Change the owner of the disallowed spy to one for which we don't have a package mapping. |
| disallowedSpy->setOwnerInfo(PID, UNLISTED_UID); |
| mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(), |
| *disallowedSpy->getInfo(), *window->getInfo()}, |
| {}, |
| 0, |
| 0}); |
| |
| tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s); |
| |
| // Remove the disallowed spy. Events are traced again. |
| mDispatcher->onWindowInfosChanged( |
| {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MatchAnyPackages) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match any package as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->add_match_any_packages(ALLOWED_PKG_1); |
| rule->add_match_any_packages(ALLOWED_PKG_2); |
| }}; |
| |
| // Just a disallowed window. Events are not traced. |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, DISALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| // Add a spy for which we don't have a package mapping. Events are still not traced. |
| auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| disallowedSpy->setOwnerInfo(PID, UNLISTED_UID); |
| disallowedSpy->setSpy(true); |
| disallowedSpy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s); |
| |
| // Add an allowed spy. Events are now traced for all packages. |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setOwnerInfo(PID, ALLOWED_UID_1); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged( |
| {{*disallowedSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({disallowedSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| // Add another disallowed spy. Events are still traced. |
| auto disallowedSpy2 = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| disallowedSpy2->setOwnerInfo(PID, DISALLOWED_UID_2); |
| disallowedSpy2->setSpy(true); |
| disallowedSpy2->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *disallowedSpy2->getInfo(), |
| *spy->getInfo(), *window->getInfo()}, |
| {}, |
| 0, |
| 0}); |
| |
| tapAndExpect({disallowedSpy, disallowedSpy2, spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MultipleMatchersInOneRule) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Match all of the following conditions as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->add_match_all_packages(ALLOWED_PKG_1); |
| rule->add_match_all_packages(ALLOWED_PKG_2); |
| rule->add_match_any_packages(ALLOWED_PKG_1); |
| rule->add_match_any_packages(DISALLOWED_PKG_1); |
| rule->set_match_secure(false); |
| rule->set_match_ime_connection_active(false); |
| }}; |
| |
| // A single window into an allowed UID. Matches all matchers. |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, ALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| // Secure window does not match. |
| window->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| // IME Connection Active does not match. |
| window->setSecure(false); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| mDispatcher->setInputMethodConnectionIsActive(true); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| // Event going to DISALLOWED_PKG_1 does not match because it's not listed in match_all_packages. |
| mDispatcher->setInputMethodConnectionIsActive(false); |
| auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1); |
| disallowedSpy->setSpy(true); |
| disallowedSpy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s); |
| |
| // Event going to ALLOWED_PKG_1 does not match because it's not listed in match_any_packages. |
| window->setOwnerInfo(PID, ALLOWED_UID_2); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({window}, Level::NONE, Level::NONE, s); |
| |
| // All conditions match. |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setOwnerInfo(PID, ALLOWED_UID_1); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, MultipleRulesMatchInOrder) { |
| InputTraceSession s{[](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Don't trace secure events |
| auto rule1 = config->add_rules(); |
| rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_NONE); |
| rule1->set_match_secure(true); |
| // Rule: Trace matched packages as COMPLETE when IME inactive |
| auto rule2 = config->add_rules(); |
| rule2->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule2->add_match_all_packages(ALLOWED_PKG_1); |
| rule2->add_match_all_packages(ALLOWED_PKG_2); |
| rule2->set_match_ime_connection_active(false); |
| // Rule: Trace the rest of the events as REDACTED |
| auto rule3 = config->add_rules(); |
| rule3->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, ALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| // Verify that the first rule that matches in the order that they are specified is the |
| // one that applies to the event. |
| mDispatcher->setInputMethodConnectionIsActive(true); |
| tapAndExpect({window}, Level::REDACTED, Level::REDACTED, s); |
| |
| mDispatcher->setInputMethodConnectionIsActive(false); |
| auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); |
| spy->setOwnerInfo(PID, ALLOWED_UID_2); |
| spy->setSpy(true); |
| spy->setTrustedOverlay(true); |
| spy->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); |
| |
| spy->setSecure(false); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); |
| |
| spy->setOwnerInfo(PID, DISALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); |
| |
| tapAndExpect({spy, window}, Level::REDACTED, Level::REDACTED, s); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, TraceInboundEvents) { |
| InputTraceSession s{[](auto& config) { |
| // Only trace inbounds events - don't trace window dispatch |
| config->set_trace_dispatcher_input_events(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Trace everything as REDACTED |
| auto rule1 = config->add_rules(); |
| rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, ALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| // Only the inbound events are traced. No dispatch events are traced. |
| tapAndExpect({window}, Level::REDACTED, Level::NONE, s); |
| |
| // Notify a down event, which should be traced. |
| const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| s.expectMotionTraced(Level::REDACTED, toMotionEvent(down)); |
| mDispatcher->notifyMotion(down); |
| auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); |
| s.expectDispatchTraced(Level::NONE, {*consumed, window}); |
| |
| // Force a cancel event to be synthesized. This should not be traced, because only inbound |
| // events are requested. |
| mDispatcher->cancelCurrentTouch(); |
| consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); |
| s.expectMotionTraced(Level::NONE, *consumed); |
| s.expectDispatchTraced(Level::NONE, {*consumed, window}); |
| |
| waitForTracerIdle(); |
| } |
| |
| TEST_F(InputTracingTest, TraceWindowDispatch) { |
| InputTraceSession s{[](auto& config) { |
| // Only trace window dispatch - don't trace event details |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Trace everything as REDACTED |
| auto rule1 = config->add_rules(); |
| rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); |
| }}; |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| window->setOwnerInfo(PID, ALLOWED_UID_1); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| |
| // Only dispatch events are traced. No inbound events are traced. |
| tapAndExpect({window}, Level::NONE, Level::REDACTED, s); |
| |
| // Notify a down event; the dispatch should be traced. |
| const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| s.expectMotionTraced(Level::NONE, toMotionEvent(down)); |
| mDispatcher->notifyMotion(down); |
| auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); |
| s.expectDispatchTraced(Level::REDACTED, {*consumed, window}); |
| |
| // Force a cancel event to be synthesized. All events that are dispatched should be traced. |
| mDispatcher->cancelCurrentTouch(); |
| consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); |
| s.expectMotionTraced(Level::NONE, *consumed); |
| s.expectDispatchTraced(Level::REDACTED, {*consumed, window}); |
| |
| waitForTracerIdle(); |
| } |
| |
| // TODO(b/336097719): Investigate flakiness and re-enable this test. |
| TEST_F(InputTracingTest, DISABLED_SimultaneousTracingSessions) { |
| auto s1 = std::make_unique<InputTraceSession>( |
| [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); }); |
| |
| auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| setFocusedWindow(window); |
| window->consumeFocusEvent(true); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); |
| keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); |
| |
| auto s2 = std::make_unique<InputTraceSession>([](auto& config) { |
| config->set_trace_dispatcher_input_events(true); |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Trace all events as REDACTED when IME inactive |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); |
| rule->set_match_ime_connection_active(false); |
| }); |
| |
| auto s3 = std::make_unique<InputTraceSession>([](auto& config) { |
| // Only trace window dispatch |
| config->set_trace_dispatcher_window_dispatch(true); |
| config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); |
| // Rule: Trace non-secure events as COMPLETE |
| auto rule = config->add_rules(); |
| rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); |
| rule->set_match_secure(false); |
| }); |
| |
| // Down event should be recorded on all traces. |
| const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(down); |
| s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(down)); |
| s2->expectMotionTraced(Level::REDACTED, toMotionEvent(down)); |
| s3->expectMotionTraced(Level::NONE, toMotionEvent(down)); |
| auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); |
| s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| s2->expectDispatchTraced(Level::REDACTED, {*consumed, window}); |
| s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| |
| // Move event when IME is active. |
| mDispatcher->setInputMethodConnectionIsActive(true); |
| const auto move1 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(move1); |
| s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move1)); |
| s2->expectMotionTraced(Level::NONE, toMotionEvent(move1)); |
| s3->expectMotionTraced(Level::NONE, toMotionEvent(move1)); |
| consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); |
| s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| s2->expectDispatchTraced(Level::NONE, {*consumed, window}); |
| s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| |
| // Move event after window became secure. |
| mDispatcher->setInputMethodConnectionIsActive(false); |
| window->setSecure(true); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| const auto move2 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(move2); |
| s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move2)); |
| s2->expectMotionTraced(Level::REDACTED, toMotionEvent(move2)); |
| s3->expectMotionTraced(Level::NONE, toMotionEvent(move2)); |
| consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); |
| s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| s2->expectDispatchTraced(Level::REDACTED, {*consumed, window}); |
| s3->expectDispatchTraced(Level::NONE, {*consumed, window}); |
| |
| waitForTracerIdle(); |
| s2.reset(); |
| |
| // Up event. |
| window->setSecure(false); |
| mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); |
| const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) |
| .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) |
| .build(); |
| mDispatcher->notifyMotion(up); |
| s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(up)); |
| s3->expectMotionTraced(Level::NONE, toMotionEvent(up)); |
| consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP)); |
| s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); |
| |
| waitForTracerIdle(); |
| s3.reset(); |
| |
| tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); |
| keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); |
| |
| waitForTracerIdle(); |
| s1.reset(); |
| } |
| |
| } // namespace android::inputdispatcher::trace |