blob: 3cc4bdd79ef60457ce58d07077e25317ce598b75 [file] [log] [blame]
/*
* 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