/*
 * 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
