diff options
193 files changed, 6728 insertions, 3237 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 83b336c62f..b99f443fa6 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -178,6 +178,7 @@ void add_mountinfo(); #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" +#define KERNEL_CONFIG "/proc/config.gz" #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" #define OTA_METADATA_DIR "/metadata/ota" @@ -1951,6 +1952,8 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { DumpFile("PSI memory", "/proc/pressure/memory"); DumpFile("PSI io", "/proc/pressure/io"); + ds.AddZipEntry(ZIP_ROOT_DIR + KERNEL_CONFIG, KERNEL_CONFIG); + RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"}, CommandOptions::WithTimeout(10).Always().DropRoot().Build()); diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index e54f9d3df7..870e8eb846 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -44,6 +44,7 @@ #include "Timeout.h" #include "utils.h" +using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hidl::base::V1_0::DebugInfo; @@ -522,27 +523,35 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; - using std::literals::chrono_literals::operator""s; - auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) { - std::map<std::string, TableEntry> entries; - for (const auto &info : infos) { - std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" + - std::string{info.instanceName.c_str()}; - entries.emplace(interfaceName, TableEntry{ - .interfaceName = interfaceName, - .transport = vintf::Transport::PASSTHROUGH, - .clientPids = info.clientPids, - }).first->second.arch |= fromBaseArchitecture(info.arch); - } - for (auto &&pair : entries) { - putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second)); - } - }); + + // The lambda function may be executed asynchrounously because it is passed to timeoutIPC, + // even though the interface function call is synchronous. + // However, there's no need to lock because if ret.isOk(), the background thread has + // already ended, so it is safe to dereference entries. + auto entries = std::make_shared<std::map<std::string, TableEntry>>(); + auto ret = + timeoutIPC(mLshal.getDebugDumpWait(), manager, &IServiceManager::debugDump, + [entries](const auto& infos) { + for (const auto& info : infos) { + std::string interfaceName = std::string{info.interfaceName.c_str()} + + "/" + std::string{info.instanceName.c_str()}; + entries->emplace(interfaceName, + TableEntry{ + .interfaceName = interfaceName, + .transport = vintf::Transport::PASSTHROUGH, + .clientPids = info.clientPids, + }) + .first->second.arch |= fromBaseArchitecture(info.arch); + } + }); if (!ret.isOk()) { err() << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } + for (auto&& pair : *entries) { + putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second)); + } return OK; } @@ -553,27 +562,40 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { using namespace ::android::hardware::details; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; - auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) { - for (const auto &info : infos) { - if (info.clientPids.size() <= 0) { - continue; - } - putEntry(HalType::PASSTHROUGH_CLIENTS, { - .interfaceName = - std::string{info.interfaceName.c_str()} + "/" + - std::string{info.instanceName.c_str()}, - .transport = vintf::Transport::PASSTHROUGH, - .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, - .clientPids = info.clientPids, - .arch = fromBaseArchitecture(info.arch) - }); - } - }); + + // The lambda function may be executed asynchrounously because it is passed to timeoutIPC, + // even though the interface function call is synchronous. + // However, there's no need to lock because if ret.isOk(), the background thread has + // already ended, so it is safe to dereference entries. + auto entries = std::make_shared<std::vector<TableEntry>>(); + auto ret = + timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::debugDump, + [entries](const auto& infos) { + for (const auto& info : infos) { + if (info.clientPids.size() <= 0) { + continue; + } + entries->emplace_back( + TableEntry{.interfaceName = + std::string{info.interfaceName.c_str()} + + "/" + + std::string{info.instanceName.c_str()}, + .transport = vintf::Transport::PASSTHROUGH, + .serverPid = info.clientPids.size() == 1 + ? info.clientPids[0] + : NO_PID, + .clientPids = info.clientPids, + .arch = fromBaseArchitecture(info.arch)}); + } + }); if (!ret.isOk()) { err() << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } + for (auto&& entry : *entries) { + putEntry(HalType::PASSTHROUGH_CLIENTS, std::move(entry)); + } return OK; } @@ -583,11 +605,14 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; } const vintf::Transport mode = vintf::Transport::HWBINDER; - hidl_vec<hidl_string> fqInstanceNames; - // copying out for timeoutIPC - auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) { - fqInstanceNames = names; - }); + + // The lambda function may be executed asynchrounously because it is passed to timeoutIPC, + // even though the interface function call is synchronous. + // However, there's no need to lock because if listRet.isOk(), the background thread has + // already ended, so it is safe to dereference fqInstanceNames. + auto fqInstanceNames = std::make_shared<hidl_vec<hidl_string>>(); + auto listRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::list, + [fqInstanceNames](const auto& names) { *fqInstanceNames = names; }); if (!listRet.isOk()) { err() << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; @@ -596,7 +621,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { Status status = OK; std::map<std::string, TableEntry> allTableEntries; - for (const auto &fqInstanceName : fqInstanceNames) { + for (const auto& fqInstanceName : *fqInstanceNames) { // create entry and default assign all fields. TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; @@ -623,7 +648,8 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, const auto pair = splitFirst(entry->interfaceName, '/'); const auto &serviceName = pair.first; const auto &instanceName = pair.second; - auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); + auto getRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::get, serviceName, + instanceName); if (!getRet.isOk()) { handleError(TRANSACTION_ERROR, "cannot be fetched from service manager:" + getRet.description()); @@ -637,30 +663,33 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, // getDebugInfo do { - DebugInfo debugInfo; - auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) { - debugInfo = received; - }); + // The lambda function may be executed asynchrounously because it is passed to timeoutIPC, + // even though the interface function call is synchronous. + // However, there's no need to lock because if debugRet.isOk(), the background thread has + // already ended, so it is safe to dereference debugInfo. + auto debugInfo = std::make_shared<DebugInfo>(); + auto debugRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getDebugInfo, + [debugInfo](const auto& received) { *debugInfo = received; }); if (!debugRet.isOk()) { handleError(TRANSACTION_ERROR, "debugging information cannot be retrieved: " + debugRet.description()); break; // skip getPidInfo } - entry->serverPid = debugInfo.pid; - entry->serverObjectAddress = debugInfo.ptr; - entry->arch = fromBaseArchitecture(debugInfo.arch); + entry->serverPid = debugInfo->pid; + entry->serverObjectAddress = debugInfo->ptr; + entry->arch = fromBaseArchitecture(debugInfo->arch); - if (debugInfo.pid != NO_PID) { - const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo.pid); + if (debugInfo->pid != NO_PID) { + const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo->pid); if (pidInfo == nullptr) { handleError(IO_ERROR, - "no information for PID " + std::to_string(debugInfo.pid) + - ", are you root?"); + "no information for PID " + std::to_string(debugInfo->pid) + + ", are you root?"); break; } - if (debugInfo.ptr != NO_PTR) { - auto it = pidInfo->refPids.find(debugInfo.ptr); + if (debugInfo->ptr != NO_PTR) { + auto it = pidInfo->refPids.find(debugInfo->ptr); if (it != pidInfo->refPids.end()) { entry->clientPids = it->second; } @@ -672,39 +701,46 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, // hash do { - ssize_t hashIndex = -1; - auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) { - for (size_t i = 0; i < c.size(); ++i) { - if (serviceName == c[i]) { - hashIndex = static_cast<ssize_t>(i); - break; - } - } - }); + // The lambda function may be executed asynchrounously because it is passed to timeoutIPC, + // even though the interface function call is synchronous. + auto hashIndexStore = std::make_shared<ssize_t>(-1); + auto ifaceChainRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::interfaceChain, + [hashIndexStore, serviceName](const auto& c) { + for (size_t i = 0; i < c.size(); ++i) { + if (serviceName == c[i]) { + *hashIndexStore = static_cast<ssize_t>(i); + break; + } + } + }); if (!ifaceChainRet.isOk()) { handleError(TRANSACTION_ERROR, "interfaceChain fails: " + ifaceChainRet.description()); break; // skip getHashChain } + // if ifaceChainRet.isOk(), the background thread has already ended, so it is safe to + // dereference hashIndex without any locking. + auto hashIndex = *hashIndexStore; if (hashIndex < 0) { handleError(BAD_IMPL, "Interface name does not exist in interfaceChain."); break; // skip getHashChain } - auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) { - if (static_cast<size_t>(hashIndex) >= hashChain.size()) { - handleError(BAD_IMPL, - "interfaceChain indicates position " + std::to_string(hashIndex) + - " but getHashChain returns " + std::to_string(hashChain.size()) + - " hashes"); - return; - } - - auto&& hashArray = hashChain[hashIndex]; - entry->hash = android::base::HexString(hashArray.data(), hashArray.size()); - }); + // See comments about hashIndex above. + auto hashChain = std::make_shared<hidl_vec<hidl_array<uint8_t, 32>>>(); + auto hashRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getHashChain, + [hashChain](const auto& ret) { *hashChain = std::move(ret); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } + if (static_cast<size_t>(hashIndex) >= hashChain->size()) { + handleError(BAD_IMPL, + "interfaceChain indicates position " + std::to_string(hashIndex) + + " but getHashChain returns " + std::to_string(hashChain->size()) + + " hashes"); + } else { + auto&& hashArray = (*hashChain)[hashIndex]; + entry->hash = android::base::HexString(hashArray.data(), hashArray.size()); + } } while (0); if (status == OK) { entry->serviceStatus = ServiceStatus::ALIVE; diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index a5f98c2a9e..6115da75b2 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -250,5 +250,17 @@ const sp<IServiceManager> &Lshal::passthroughManager() const { return mPassthroughManager; } +void Lshal::setWaitTimeForTest(std::chrono::milliseconds ipcCallWait, + std::chrono::milliseconds debugDumpWait) { + mIpcCallWait = ipcCallWait; + mDebugDumpWait = debugDumpWait; +} +std::chrono::milliseconds Lshal::getIpcCallWait() const { + return mIpcCallWait; +} +std::chrono::milliseconds Lshal::getDebugDumpWait() const { + return mDebugDumpWait; +} + } // namespace lshal } // namespace android diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index 50279d4d7a..cb2820c535 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -16,6 +16,7 @@ #pragma once +#include <chrono> #include <iostream> #include <string> @@ -58,6 +59,11 @@ public: void forEachCommand(const std::function<void(const Command* c)>& f) const; + void setWaitTimeForTest(std::chrono::milliseconds ipcCallWait, + std::chrono::milliseconds debugDumpWait); + std::chrono::milliseconds getIpcCallWait() const; + std::chrono::milliseconds getDebugDumpWait() const; + private: Status parseArgs(const Arg &arg); @@ -70,6 +76,9 @@ private: std::vector<std::unique_ptr<Command>> mRegisteredCommands; + std::chrono::milliseconds mIpcCallWait{500}; + std::chrono::milliseconds mDebugDumpWait{10000}; + DISALLOW_COPY_AND_ASSIGN(Lshal); }; diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h index e8d22d9b58..805e8dc43a 100644 --- a/cmds/lshal/Timeout.h +++ b/cmds/lshal/Timeout.h @@ -27,8 +27,6 @@ namespace android { namespace lshal { -static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500}; - class BackgroundTaskState { public: explicit BackgroundTaskState(std::function<void(void)> &&func) @@ -96,12 +94,5 @@ timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Funct return ret; } -template<class Function, class I, class... Args> -typename std::result_of<Function(I *, Args...)>::type -timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) { - return timeoutIPC(IPC_CALL_WAIT, interfaceObject, func, args...); -} - - } // namespace lshal } // namespace android diff --git a/include/ftl/details/function.h b/include/ftl/details/function.h new file mode 100644 index 0000000000..35c5a8b302 --- /dev/null +++ b/include/ftl/details/function.h @@ -0,0 +1,135 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#include <array> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <type_traits> + +namespace android::ftl::details { + +// The maximum allowed value for the template argument `N` in +// `ftl::Function<F, N>`. +constexpr size_t kFunctionMaximumN = 14; + +// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member +// function type `Ret(Args...)`. + +template <typename> +struct remove_member_function_pointer; + +template <typename Class, typename Ret, typename... Args> +struct remove_member_function_pointer<Ret (Class::*)(Args...)> { + using type = Ret(Args...); +}; + +template <typename Class, typename Ret, typename... Args> +struct remove_member_function_pointer<Ret (Class::*)(Args...) const> { + using type = Ret(Args...); +}; + +template <auto MemberFunction> +using remove_member_function_pointer_t = + typename remove_member_function_pointer<decltype(MemberFunction)>::type; + +// Helper functions for binding to the supported targets. + +template <typename Ret, typename... Args> +auto bind_opaque_no_op() -> Ret (*)(void*, Args...) { + return [](void*, Args...) -> Ret { + if constexpr (!std::is_void_v<Ret>) { + return Ret{}; + } + }; +} + +template <typename F, typename Ret, typename... Args> +auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) { + return [](void* opaque, Args... args) -> Ret { + return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...); + }; +} + +template <auto MemberFunction, typename Class, typename Ret, typename... Args> +auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) { + return [instance](Args... args) -> Ret { + return std::invoke(MemberFunction, instance, std::forward<Args>(args)...); + }; +} + +template <auto FreeFunction, typename Ret, typename... Args> +auto bind_free_function(Ret (*)(Args...) = nullptr) { + return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); }; +} + +// Traits class for the opaque storage used by Function. + +template <std::size_t N> +struct function_opaque_storage { + // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, + // which allows a lambda with zero or one capture args. + using type = std::array<std::intptr_t, N + 1>; + + template <typename S> + static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>; + + template <typename S> + static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>; + + template <typename S> + static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); + + template <typename S> + static constexpr bool require_alignment_compatible = + std::alignment_of_v<S> <= std::alignment_of_v<type>; + + // Copies `src` into the opaque storage, and returns that storage. + template <typename S> + static type opaque_copy(const S& src) { + // TODO: Replace with C++20 concepts/constraints which can give more details. + static_assert(require_trivially_copyable<S>, + "ftl::Function can only store lambdas that capture trivially copyable data."); + static_assert( + require_trivially_destructible<S>, + "ftl::Function can only store lambdas that capture trivially destructible data."); + static_assert(require_will_fit_in_opaque_storage<S>, + "ftl::Function has limited storage for lambda captured state. Maybe you need to " + "increase N?"); + static_assert(require_alignment_compatible<S>); + + type opaque; + std::memcpy(opaque.data(), &src, sizeof(S)); + return opaque; + } +}; + +// Traits class to help determine the template parameters to use for a ftl::Function, given a +// function object. + +template <typename F, typename = decltype(&F::operator())> +struct function_traits { + // The function type `F` with which to instantiate the `Function<F, N>` template. + using type = remove_member_function_pointer_t<&F::operator()>; + + // The (minimum) size `N` with which to instantiate the `Function<F, N>` template. + static constexpr std::size_t size = + (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); +}; + +} // namespace android::ftl::details diff --git a/include/ftl/function.h b/include/ftl/function.h new file mode 100644 index 0000000000..3538ca4eae --- /dev/null +++ b/include/ftl/function.h @@ -0,0 +1,297 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> + +#include <ftl/details/function.h> + +namespace android::ftl { + +// ftl::Function<F, N> is a container for function object, and can mostly be used in place of +// std::function<F>. +// +// Unlike std::function<F>, a ftl::Function<F, N>: +// +// * Uses a static amount of memory (controlled by N), and never any dynamic allocation. +// * Satisfies the std::is_trivially_copyable<> trait. +// * Satisfies the std::is_trivially_destructible<> trait. +// +// However those same limits are also required from the contained function object in turn. +// +// The size of a ftl::Function<F, N> is guaranteed to be: +// +// sizeof(std::intptr_t) * (N + 2) +// +// A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>. +// Trying to convert the other way leads to a compilation error. +// +// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns +// false in this state. It is undefined behavior to attempt to invoke the function in this state. +// +// The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the +// ftl::Function to be non-empty, with a function that when called does nothing except +// default-constructs a return value. +// +// The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the +// values of F and N from the arguments it is given. +// +// The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that +// deduction, and also allow for implicit argument conversion if the target being called needs them. +// +// The construction helpers allow any of the following types of functions to be stored: +// +// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small +// capture, or other "functor". The requirements are: +// +// 1) The function object must be trivial to destroy (in fact, the destructor will never +// actually be called once copied to the internal storage). +// 2) The function object must be trivial to copy (the raw bytes will be copied as the +// ftl::Function<F, N> is copied/moved). +// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), +// and it cannot require stricter alignment than alignof(std::intptr_t). +// +// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is +// enough to capture `this`, which is why N=0 is the default. +// +// * A member function, with the address passed as the template value argument to the construction +// helper function, along with the instance pointer needed to invoke it passed as an ordinary +// argument. +// +// ftl::make_function<&Class::member_function>(this); +// +// Note that the indicated member function will be invoked non-virtually. If you need it to be +// invoked virtually, you should invoke it yourself with a small lambda like so: +// +// ftl::function([this] { virtual_member_function(); }); +// +// * An ordinary function ("free function"), with the address of the function passed as a template +// value argument. +// +// ftl::make_function<&std::atoi>(); +// +// As with the member function helper, as the function is known at compile time, it will be called +// directly. +// +// Example usage: +// +// class MyClass { +// public: +// void on_event() const {} +// int on_string(int*, std::string_view) { return 1; } +// +// auto get_function() { +// return ftl::function([this] { on_event(); }); +// } +// } cls; +// +// // A function container with no arguments, and returning no value. +// ftl::Function<void()> f; +// +// // Construct a ftl::Function containing a small lambda. +// f = cls.get_function(); +// +// // Construct a ftl::Function that calls `cls.on_event()`. +// f = ftl::function<&MyClass::on_event>(&cls); +// +// // Create a do-nothing function. +// f = ftl::no_op; +// +// // Invoke the contained function. +// f(); +// +// // Also invokes it. +// std::invoke(f); +// +// // Create a typedef to give a more meaningful name and bound the size. +// using MyFunction = ftl::Function<int(std::string_view), 2>; +// int* ptr = nullptr; +// auto f1 = MyFunction::make_function( +// [cls = &cls, ptr](std::string_view sv) { +// return cls->on_string(ptr, sv); +// }); +// int r = f1("abc"sv); +// +// // Returns a default-constructed int (0). +// f1 = ftl::no_op; +// r = f1("abc"sv); +// assert(r == 0); + +template <typename F, std::size_t N = 0> +class Function; + +// Used to construct a Function that does nothing. +struct NoOpTag {}; + +constexpr NoOpTag no_op; + +// Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are. +template <typename> +struct is_function : public std::false_type {}; + +template <typename F, std::size_t N> +struct is_function<Function<F, N>> : public std::true_type {}; + +template <typename T> +constexpr bool is_function_v = is_function<T>::value; + +template <typename Ret, typename... Args, std::size_t N> +class Function<Ret(Args...), N> final { + // Enforce a valid size, with an arbitrary maximum allowed size for the container of + // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. + static_assert(N <= details::kFunctionMaximumN); + + using OpaqueStorageTraits = details::function_opaque_storage<N>; + + public: + // Defining result_type allows ftl::Function to be substituted for std::function. + using result_type = Ret; + + // Constructs an empty ftl::Function. + Function() = default; + + // Constructing or assigning from nullptr_t also creates an empty ftl::Function. + Function(std::nullptr_t) {} + Function& operator=(std::nullptr_t) { return *this = Function(nullptr); } + + // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which + // returns a default constructed return value. + Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {} + Function& operator=(NoOpTag) { return *this = Function(no_op); } + + // Constructing/assigning from a function object stores a copy of that function object, however: + // * It must be trivially copyable, as the implementation makes a copy with memcpy(). + // * It must be trivially destructible, as the implementation doesn't destroy the copy! + // * It must fit in the limited internal storage, which enforces size/alignment restrictions. + + template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> + Function(const F& f) + : opaque_(OpaqueStorageTraits::opaque_copy(f)), + function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {} + + template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> + Function& operator=(const F& f) noexcept { + return *this = Function{OpaqueStorageTraits::opaque_copy(f), + details::bind_opaque_function_object<F, Ret, Args...>(f)}; + } + + // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. + + template <std::size_t M> + Function(const Function<Ret(Args...), M>& other) + : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {} + + template <std::size_t M> + auto& operator=(const Function<Ret(Args...), M>& other) { + return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_}; + } + + // Returns true if a function is set. + explicit operator bool() const { return function_ != nullptr; } + + // Checks if the other function has the same contents as this one. + bool operator==(const Function& other) const { + return other.opaque_ == opaque_ && other.function_ == function_; + } + bool operator!=(const Function& other) const { return !operator==(other); } + + // Alternative way of testing for a function being set. + bool operator==(std::nullptr_t) const { return function_ == nullptr; } + bool operator!=(std::nullptr_t) const { return function_ != nullptr; } + + // Invokes the function. + Ret operator()(Args... args) const { + return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...); + } + + // Creation helper for function objects, such as lambdas. + template <typename F> + static auto make(const F& f) -> decltype(Function{f}) { + return Function{f}; + } + + // Creation helper for a class pointer and a compile-time chosen member function to call. + template <auto MemberFunction, typename Class> + static auto make(Class* instance) -> decltype(Function{ + details::bind_member_function<MemberFunction>(instance, + static_cast<Ret (*)(Args...)>(nullptr))}) { + return Function{details::bind_member_function<MemberFunction>( + instance, static_cast<Ret (*)(Args...)>(nullptr))}; + } + + // Creation helper for a compile-time chosen free function to call. + template <auto FreeFunction> + static auto make() -> decltype(Function{ + details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) { + return Function{ + details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}; + } + + private: + // Needed so a Function<F, M> can be converted to a Function<F, N>. + template <typename, std::size_t> + friend class Function; + + // The function pointer type of function stored in `function_`. The first argument is always + // `&opaque_`. + using StoredFunction = Ret(void*, Args...); + + // The type of the opaque storage, used to hold an appropriate function object. + // The type stored here is ONLY known to the StoredFunction. + // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. + using OpaqueStorage = typename OpaqueStorageTraits::type; + + // Internal constructor for creating from a raw opaque blob + function pointer. + Function(const OpaqueStorage& opaque, StoredFunction* function) + : opaque_(opaque), function_(function) {} + + // Note: `mutable` so that `operator() const` can use it. + mutable OpaqueStorage opaque_{}; + StoredFunction* function_{nullptr}; +}; + +// Makes a ftl::Function given a function object `F`. +template <typename F, typename T = details::function_traits<F>> +Function(const F&) -> Function<typename T::type, T::size>; + +template <typename F> +auto make_function(const F& f) -> decltype(Function{f}) { + return Function{f}; +} + +// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. +template <auto MemberFunction, typename Class> +auto make_function(Class* instance) + -> decltype(Function{details::bind_member_function<MemberFunction>( + instance, + static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) { + return Function{details::bind_member_function<MemberFunction>( + instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}; +} + +// Makes a ftl::Function given an ordinary free function. +template <auto FreeFunction> +auto make_function() -> decltype(Function{ + details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) { + return Function{ + details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}; +} + +} // namespace android::ftl diff --git a/include/input/Input.h b/include/input/Input.h index 1c4ea6b416..7b253a53ef 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -515,6 +515,8 @@ struct PointerProperties { PointerProperties& operator=(const PointerProperties&) = default; }; +std::ostream& operator<<(std::ostream& out, const PointerProperties& properties); + // TODO(b/211379801) : Use a strong type from ftl/mixins.h instead using DeviceId = int32_t; diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 83fffa37c6..3470be4dce 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -117,11 +117,12 @@ std::string dumpMapKeys(const std::map<K, V>& map, template <typename T> std::string dumpVector(const std::vector<T>& values, std::string (*valueToString)(const T&) = constToString) { - std::string dump = valueToString(values[0]); - for (size_t i = 1; i < values.size(); i++) { - dump += ", " + valueToString(values[i]); + std::string out; + for (const auto& value : values) { + out += out.empty() ? "[" : ", "; + out += valueToString(value); } - return dump; + return out.empty() ? "[]" : (out + "]"); } const char* toString(bool value); diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index f1cc5f05ec..ae0fb018ef 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -22,23 +22,51 @@ package { } cc_library_headers { - name: "libbinder_headers", + name: "libbinder_headers_base", export_include_dirs: ["include"], vendor_available: true, recovery_available: true, host_supported: true, - // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, header_libs: [ - "libbase_headers", "libbinder_headers_platform_shared", + ], + export_header_lib_headers: [ + "libbinder_headers_platform_shared", + ], + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + min_sdk_version: "29", + target: { + darwin: { + enabled: false, + }, + }, + visibility: [ + ":__subpackages__", + ], +} + +cc_library_headers { + name: "libbinder_headers", + vendor_available: true, + recovery_available: true, + host_supported: true, + native_bridge_supported: true, + + header_libs: [ + "libbase_headers", + "libbinder_headers_base", "libcutils_headers", "libutils_headers", ], export_header_lib_headers: [ "libbase_headers", - "libbinder_headers_platform_shared", + "libbinder_headers_base", "libcutils_headers", "libutils_headers", ], @@ -87,6 +115,7 @@ cc_defaults { "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", + "RpcTransportRaw.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", @@ -94,17 +123,8 @@ cc_defaults { "file.cpp", ], - shared_libs: [ - "libcutils", - "libutils", - ], - - static_libs: [ - "libbase", - ], - header_libs: [ - "libbinder_headers", + "libbinder_headers_base", ], cflags: [ @@ -131,7 +151,6 @@ cc_defaults { srcs: [ "OS_android.cpp", "OS_unix_base.cpp", - "RpcTransportRaw.cpp", ], target: { @@ -156,11 +175,18 @@ cc_defaults { }, shared_libs: [ + "libcutils", "liblog", + "libutils", + ], + + static_libs: [ + "libbase", ], header_libs: [ "jni_headers", + "libbinder_headers", ], export_header_lib_headers: [ @@ -217,17 +243,24 @@ cc_defaults { host_supported: true, header_libs: [ + "libbinder_headers_base", "liblog_stub", "trusty_mock_headers", ], + shared_libs: [ + "libutils_binder_sdk", + ], + cflags: [ "-DBINDER_RPC_SINGLE_THREADED", "-DBINDER_ENABLE_LIBLOG_ASSERT", "-DBINDER_DISABLE_NATIVE_HANDLE", "-DBINDER_DISABLE_BLOB", "-DBINDER_NO_LIBBASE", + // TODO: switch to "vendor: true" rather than copying this // Trusty libbinder uses vendor stability for its binders + "-D__ANDROID_VENDOR__", "-D__ANDROID_VNDK__", "-U__ANDROID__", "-D__TRUSTY__", @@ -351,6 +384,44 @@ cc_library { afdo: true, } +cc_library_host_shared { + name: "libbinder_sdk", + + defaults: [ + "libbinder_common_defaults", + ], + + shared_libs: [ + "libutils_binder_sdk", + ], + + cflags: [ + "-DBINDER_ENABLE_LIBLOG_ASSERT", + "-DBINDER_DISABLE_NATIVE_HANDLE", + "-DBINDER_DISABLE_BLOB", + "-DBINDER_NO_LIBBASE", + ], + + header_libs: [ + "liblog_stub", + ], + + srcs: [ + "OS_non_android_linux.cpp", + "OS_unix_base.cpp", + ], + + visibility: [ + ":__subpackages__", + ], + + target: { + windows: { + enabled: false, + }, + }, +} + cc_library_static { name: "libbinder_rpc_no_kernel", vendor_available: true, diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 9341eff91e..b92e504a9a 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -67,28 +67,28 @@ namespace android { // Static const and functions will be optimized out if not used, // when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out. -static const char *kReturnStrings[] = { - "BR_ERROR", - "BR_OK", - "BR_TRANSACTION", - "BR_REPLY", - "BR_ACQUIRE_RESULT", - "BR_DEAD_REPLY", - "BR_TRANSACTION_COMPLETE", - "BR_INCREFS", - "BR_ACQUIRE", - "BR_RELEASE", - "BR_DECREFS", - "BR_ATTEMPT_ACQUIRE", - "BR_NOOP", - "BR_SPAWN_LOOPER", - "BR_FINISHED", - "BR_DEAD_BINDER", - "BR_CLEAR_DEATH_NOTIFICATION_DONE", - "BR_FAILED_REPLY", - "BR_FROZEN_REPLY", - "BR_ONEWAY_SPAM_SUSPECT", - "BR_TRANSACTION_SEC_CTX", +static const char* kReturnStrings[] = { + "BR_ERROR", + "BR_OK", + "BR_TRANSACTION/BR_TRANSACTION_SEC_CTX", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE", + "BR_FAILED_REPLY", + "BR_FROZEN_REPLY", + "BR_ONEWAY_SPAM_SUSPECT", + "BR_TRANSACTION_PENDING_FROZEN", }; static const char *kCommandStrings[] = { diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp new file mode 100644 index 0000000000..b525d1ac34 --- /dev/null +++ b/libs/binder/OS_non_android_linux.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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 "OS.h" + +#include <log/log.h> + +#include <syscall.h> +#include <cstdarg> + +#ifdef __ANDROID__ +#error "This module is not intended for Android, just bare Linux" +#endif +#ifdef __APPLE__ +#error "This module is not intended for MacOS" +#endif +#ifdef _WIN32 +#error "This module is not intended for Windows" +#endif + +namespace android::binder::os { + +void trace_begin(uint64_t, const char*) {} + +void trace_end(uint64_t) {} + +uint64_t GetThreadId() { + return syscall(__NR_gettid); +} + +bool report_sysprop_change() { + return false; +} + +} // namespace android::binder::os + +int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + return 1; +} diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp index c432b3af29..665dfea456 100644 --- a/libs/binder/Stability.cpp +++ b/libs/binder/Stability.cpp @@ -73,6 +73,14 @@ void Stability::tryMarkCompilationUnit(IBinder* binder) { (void)setRepr(binder, getLocalLevel(), REPR_NONE); } +// after deprecation of the VNDK, these should be aliases. At some point +// all references to __ANDROID_VNDK__ should be replaced by __ANDROID_VENDOR__ +// but for right now, check that this condition holds because some +// places check __ANDROID_VNDK__ and some places check __ANDROID_VENDOR__ +#if defined(__ANDROID_VNDK__) != defined(__ANDROID_VENDOR__) +#error "__ANDROID_VNDK__ and __ANDROID_VENDOR__ should be aliases" +#endif + Stability::Level Stability::getLocalLevel() { #ifdef __ANDROID_APEX__ #error "APEX can't use libbinder (must use libbinder_ndk)" diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 9347ce47a5..dc5b1a1712 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -62,8 +62,13 @@ public: /** * Returns the PID of the process which has made the current binder - * call. If not in a binder call, this will return getpid. If the - * call is oneway, this will return 0. + * call. If not in a binder call, this will return getpid. + * + * Warning: oneway transactions do not receive PID. Even if you expect + * a transaction to be synchronous, a misbehaving client could send it + * as an asynchronous call and result in a 0 PID here. Additionally, if + * there is a race and the calling process dies, the PID may still be + * 0 for a synchronous call. */ [[nodiscard]] pid_t getCallingPid() const; diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index ddd82e8ef7..cb44c58c2c 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -20,8 +20,14 @@ #include <binder/RpcServer.h> #include <binder/RpcSession.h> #include <binder/unique_fd.h> + +#ifndef __TRUSTY__ #include <cutils/sockets.h> +#endif + +#ifdef __linux__ #include <linux/vm_sockets.h> +#endif // __linux__ using android::OK; using android::RpcServer; @@ -74,6 +80,7 @@ RpcSession::FileDescriptorTransportMode toTransportMode( extern "C" { +#ifndef __TRUSTY__ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) { auto server = RpcServer::make(); @@ -147,6 +154,7 @@ ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned server->setRootObject(AIBinder_toPlatformBinder(service)); return createObjectHandle<ARpcServer>(server); } +#endif // __TRUSTY__ void ARpcServer_setSupportedFileDescriptorTransportModes( ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[], @@ -187,6 +195,7 @@ void ARpcSession_free(ARpcSession* handle) { freeObjectHandle<RpcSession>(handle); } +#ifndef __TRUSTY__ AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) { auto session = handleToStrongPointer<RpcSession>(handle); if (status_t status = session->setupVsockClient(cid, port); status != OK) { @@ -234,13 +243,14 @@ AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsign } return AIBinder_fromPlatformBinder(session->getRootObject()); } +#endif // __TRUSTY__ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param), void* param) { auto session = handleToStrongPointer<RpcSession>(handle); auto request = [=] { return unique_fd{requestFd(param)}; }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { - ALOGE("Failed to set up vsock client. error: %s", statusToString(status).c_str()); + ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h index 3b656076b3..91c9632c1b 100644 --- a/libs/binder/liblog_stub/include/log/log.h +++ b/libs/binder/liblog_stub/include/log/log.h @@ -35,15 +35,19 @@ constexpr bool __android_log_stub_is_loggable(android_LogPriority priority) { return ANDROID_LOG_STUB_MIN_PRIORITY <= priority; } -int __android_log_print(int prio, const char* tag, const char* fmt, ...) - __attribute__((format(printf, 3, 4))) #ifdef ANDROID_LOG_STUB_WEAK_PRINT - __attribute__((weak)) +#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT __android_log_print +#define __ANDROID_LOG_STUB_PRINT_ATTR __attribute__((weak)) +#else +#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT true +#define __ANDROID_LOG_STUB_PRINT_ATTR #endif - ; + +int __android_log_print(int prio, const char* tag, const char* fmt, ...) + __attribute__((format(printf, 3, 4))) __ANDROID_LOG_STUB_PRINT_ATTR; #define IF_ALOG(priority, tag) \ - if (__android_log_stub_is_loggable(ANDROID_##priority) && __android_log_print) + if (__android_log_stub_is_loggable(ANDROID_##priority) && __ANDROID_LOG_STUB_IS_PRINT_PRESENT) #define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) #define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) #define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index db2d2c1b09..b1ab7b0f9a 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -390,6 +390,12 @@ uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); * calling process dies and is replaced with another process with elevated permissions and the same * PID. * + * Warning: oneway transactions do not receive PID. Even if you expect + * a transaction to be synchronous, a misbehaving client could send it + * as a synchronous call and result in a 0 PID here. Additionally, if + * there is a race and the calling process dies, the PID may still be + * 0 for a synchronous call. + * * Available since API level 29. * * \return calling pid or the current process's PID if this thread isn't processing a transaction. diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index a9573850f1..163f000ac8 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -16,8 +16,10 @@ //! API for RPC Binder services. +#[cfg(not(target_os = "trusty"))] mod server; mod session; +#[cfg(not(target_os = "trusty"))] pub use server::{RpcServer, RpcServerRef}; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 79a951073e..09688a21a7 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -17,11 +17,8 @@ use binder::unstable_api::new_spibinder; use binder::{FromIBinder, SpIBinder, StatusCode, Strong}; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; -use std::os::{ - raw::{c_int, c_void}, - unix::io::{AsRawFd, BorrowedFd, RawFd}, -}; +use std::os::fd::RawFd; +use std::os::raw::{c_int, c_void}; pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode; @@ -87,6 +84,7 @@ impl RpcSessionRef { } /// Connects to an RPC Binder server over vsock for a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_vsock_client<T: FromIBinder + ?Sized>( &self, cid: u32, @@ -106,11 +104,12 @@ impl RpcSessionRef { /// Connects to an RPC Binder server over a names Unix Domain Socket for /// a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>( &self, socket_name: &str, ) -> Result<Strong<T>, StatusCode> { - let socket_name = match CString::new(socket_name) { + let socket_name = match std::ffi::CString::new(socket_name) { Ok(s) => s, Err(e) => { log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); @@ -131,10 +130,12 @@ impl RpcSessionRef { /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket /// for a particular interface. + #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>( &self, - bootstrap_fd: BorrowedFd, + bootstrap_fd: std::os::fd::BorrowedFd, ) -> Result<Strong<T>, StatusCode> { + use std::os::fd::AsRawFd; // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take // ownership of bootstrap_fd. The returned AIBinder has correct // reference count, and the ownership can safely be taken by new_spibinder. @@ -148,12 +149,13 @@ impl RpcSessionRef { } /// Connects to an RPC Binder server over inet socket at the given address and port. + #[cfg(not(target_os = "trusty"))] pub fn setup_inet_client<T: FromIBinder + ?Sized>( &self, address: &str, port: u32, ) -> Result<Strong<T>, StatusCode> { - let address = match CString::new(address) { + let address = match std::ffi::CString::new(address) { Ok(s) => s, Err(e) => { log::error!("Cannot convert {} to CString. Error: {:?}", address, e); @@ -173,6 +175,22 @@ impl RpcSessionRef { Self::get_interface(service) } + #[cfg(target_os = "trusty")] + pub fn setup_trusty_client<T: FromIBinder + ?Sized>( + &self, + port: &std::ffi::CStr, + ) -> Result<Strong<T>, StatusCode> { + self.setup_preconnected_client(|| { + let h = tipc::Handle::connect(port) + .expect("Failed to connect to service port {SERVICE_PORT}"); + + // Do not close the handle at the end of the scope + let fd = h.as_raw_fd(); + core::mem::forget(h); + Some(fd) + }) + } + /// Connects to an RPC Binder server, using the given callback to get (and /// take ownership of) file descriptors already connected to it. pub fn setup_preconnected_client<T: FromIBinder + ?Sized>( diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index a3a2562eb1..8a06274e9c 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -101,13 +101,16 @@ impl ThreadState { /// dies and is replaced with another process with elevated permissions and /// the same PID. /// + /// Warning: oneway transactions do not receive PID. Even if you expect + /// a transaction to be synchronous, a misbehaving client could send it + /// as a synchronous call and result in a 0 PID here. Additionally, if + /// there is a race and the calling process dies, the PID may still be + /// 0 for a synchronous call. + /// /// Available since API level 29. /// /// \return calling pid or the current process's PID if this thread isn't /// processing a transaction. - /// - /// If the transaction being processed is a oneway transaction, then this - /// method will return 0. pub fn get_calling_pid() -> pid_t { // Safety: Safe FFI unsafe { sys::AIBinder_getCallingPid() } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index e4d4de86bc..dd2be94a76 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -256,6 +256,12 @@ cc_defaults { // contention on the device. b/276820894 test_options: { unit_test: false, + test_runner_options: [ + { + name: "native-test-timeout", + value: "10m", + }, + ], }, test_suites: ["general-tests"], @@ -830,6 +836,7 @@ cc_defaults { ], // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer hotlists: ["4637097"], + use_for_presubmit: true, }, } diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index 9869bf3460..a8dabc3a1d 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -126,8 +126,3 @@ int __android_log_print(int prio [[maybe_unused]], const char* tag, const char* return 1; } - -// TODO(b/285204695): remove once trusty mock doesn't depend on libbase -extern "C" int __android_log_buf_print(int, int, const char*, const char*, ...) { - return -ENOSYS; -} diff --git a/libs/binder/trusty/binder_rpc_unstable/rules.mk b/libs/binder/trusty/binder_rpc_unstable/rules.mk new file mode 100644 index 0000000000..d8dbce54e8 --- /dev/null +++ b/libs/binder/trusty/binder_rpc_unstable/rules.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := \ + $(LIBBINDER_DIR)/libbinder_rpc_unstable.cpp \ + +MODULE_EXPORT_INCLUDES += \ + $(LIBBINDER_DIR)/include_rpc_unstable \ + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + trusty/user/base/lib/libstdc++-trusty \ + +include make/library.mk diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk index 788184d2ce..5cbe0afafa 100644 --- a/libs/binder/trusty/kernel/rules.mk +++ b/libs/binder/trusty/kernel/rules.mk @@ -69,6 +69,7 @@ GLOBAL_COMPILEFLAGS += \ -DBINDER_DISABLE_NATIVE_HANDLE \ -DBINDER_DISABLE_BLOB \ -DBINDER_NO_LIBBASE \ + -D__ANDROID_VENDOR__ \ -D__ANDROID_VNDK__ \ MODULE_DEPS += \ diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk index e0f821f2c9..f2f140d9ba 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -72,6 +72,7 @@ MODULE_EXPORT_COMPILEFLAGS += \ -DBINDER_DISABLE_NATIVE_HANDLE \ -DBINDER_DISABLE_BLOB \ -DBINDER_NO_LIBBASE \ + -D__ANDROID_VENDOR__ \ -D__ANDROID_VNDK__ \ # libbinder has some deprecated declarations that we want to produce warnings diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp new file mode 100644 index 0000000000..6f96566618 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2023 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 <binder_rpc_unstable.hpp> diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs new file mode 100644 index 0000000000..c7036f45f7 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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. + */ + +//! Generated Rust bindings to binder_rpc_unstable + +#[allow(bad_style)] +mod sys { + include!(env!("BINDGEN_INC_FILE")); +} + +pub use sys::*; diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk new file mode 100644 index 0000000000..ef1b7c3cf8 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/lib.rs + +MODULE_CRATE_NAME := binder_rpc_unstable_bindgen + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/binder_rpc_unstable \ + $(LIBBINDER_DIR)/trusty/ndk \ + $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/trusty-sys \ + +MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp + +MODULE_BINDGEN_FLAGS += \ + --blocklist-type="AIBinder" \ + --raw-line="use binder_ndk_sys::AIBinder;" \ + --rustified-enum="ARpcSession_FileDescriptorTransportMode" \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk new file mode 100644 index 0000000000..76f3b9401f --- /dev/null +++ b/libs/binder/trusty/rust/rpcbinder/rules.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2023 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LIBBINDER_DIR)/rust/rpcbinder/src/lib.rs + +MODULE_CRATE_NAME := rpcbinder + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + $(LIBBINDER_DIR)/trusty/rust \ + $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ + external/rust/crates/foreign-types \ + trusty/user/base/lib/tipc/rust \ + trusty/user/base/lib/trusty-sys \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk index be90df3147..6de7eb5b3c 100644 --- a/libs/binder/trusty/rust/rules.mk +++ b/libs/binder/trusty/rust/rules.mk @@ -26,6 +26,7 @@ MODULE_LIBRARY_DEPS += \ $(LIBBINDER_DIR)/trusty \ $(LIBBINDER_DIR)/trusty/ndk \ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ external/rust/crates/downcast-rs \ trusty/user/base/lib/trusty-sys \ diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index ea1b5e4998..918680d6a7 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -17,6 +17,7 @@ cc_test { "enum_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", + "function_test.cpp", "future_test.cpp", "match_test.cpp", "mixins_test.cpp", diff --git a/libs/ftl/function_test.cpp b/libs/ftl/function_test.cpp new file mode 100644 index 0000000000..91b5e08041 --- /dev/null +++ b/libs/ftl/function_test.cpp @@ -0,0 +1,379 @@ +/* + * Copyright 2022 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 <ftl/function.h> +#include <gtest/gtest.h> + +#include <array> +#include <cstddef> +#include <cstdint> +#include <string_view> +#include <type_traits> + +namespace android::test { +namespace { + +// Create an alias to composite requirements defined by the trait class `T` for easier testing. +template <typename T, typename S> +inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> && + T::template require_trivially_destructible<S> && + T::template require_will_fit_in_opaque_storage<S> && + T::template require_alignment_compatible<S>); + +// `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes +template <size_t I, size_t J = 0> +struct KnownSizeFunctionObject { + using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>; + void operator()() const {}; + Data data{}; +}; + +} // namespace + +// static_assert the expected type traits +static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>); +static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>); +static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>); +static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>); + +template <typename T> +using function_traits = ftl::details::function_traits<T>; + +// static_assert that the expected value of N is used for known function object sizes. +static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1); +static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1); +static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2); + +// Check that is_function_v works +static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>); +static_assert(!ftl::is_function_v<std::function<void()>>); +static_assert(ftl::is_function_v<ftl::Function<void()>>); + +// static_assert what can and cannot be stored inside the opaque storage + +template <size_t N> +using function_opaque_storage = ftl::details::function_opaque_storage<N>; + +// Function objects can be stored if they fit. +static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>); +static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>); +static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>); + +static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>); +static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>); + +static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>); +static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>); + +// Another opaque storage can be stored if it fits. This property is used to copy smaller +// ftl::Functions into larger ones. +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>); +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>); +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>); +static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>); + +// Function objects that aren't trivially copyable or destroyable cannot be stored. +auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); }; +static_assert( + !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>); + +// Keep in sync with "Example usage" in header file. +TEST(Function, Example) { + using namespace std::string_view_literals; + + class MyClass { + public: + void on_event() const {} + int on_string(int*, std::string_view) { return 1; } + + auto get_function() { + return ftl::make_function([this] { on_event(); }); + } + } cls; + + // A function container with no arguments, and returning no value. + ftl::Function<void()> f; + + // Construct a ftl::Function containing a small lambda. + f = cls.get_function(); + + // Construct a ftl::Function that calls `cls.on_event()`. + f = ftl::make_function<&MyClass::on_event>(&cls); + + // Create a do-nothing function. + f = ftl::no_op; + + // Invoke the contained function. + f(); + + // Also invokes it. + std::invoke(f); + + // Create a typedef to give a more meaningful name and bound the size. + using MyFunction = ftl::Function<int(std::string_view), 2>; + int* ptr = nullptr; + auto f1 = + MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); }); + int r = f1("abc"sv); + + // Returns a default-constructed int (0). + f1 = ftl::no_op; + r = f1("abc"sv); + EXPECT_EQ(r, 0); +} + +TEST(Function, BasicOperations) { + // Default constructible. + ftl::Function<int()> f; + + // Compares as empty + EXPECT_FALSE(f); + EXPECT_TRUE(f == nullptr); + EXPECT_FALSE(f != nullptr); + EXPECT_TRUE(ftl::Function<int()>() == f); + EXPECT_FALSE(ftl::Function<int()>() != f); + + // Assigning no_op sets it to not empty. + f = ftl::no_op; + + // Verify it can be called, and that it returns a default constructed value. + EXPECT_EQ(f(), 0); + + // Comparable when non-empty. + EXPECT_TRUE(f); + EXPECT_FALSE(f == nullptr); + EXPECT_TRUE(f != nullptr); + EXPECT_FALSE(ftl::Function<int()>() == f); + EXPECT_TRUE(ftl::Function<int()>() != f); + + // Constructing from nullptr means empty. + f = ftl::Function<int()>{nullptr}; + EXPECT_FALSE(f); + + // Assigning nullptr means it is empty. + f = nullptr; + EXPECT_FALSE(f); + + // Move construction + f = ftl::no_op; + ftl::Function<int()> g{std::move(f)}; + EXPECT_TRUE(g != nullptr); + + // Move assignment + f = nullptr; + f = std::move(g); + EXPECT_TRUE(f != nullptr); + + // Copy construction + ftl::Function<int()> h{f}; + EXPECT_TRUE(h != nullptr); + + // Copy assignment + g = h; + EXPECT_TRUE(g != nullptr); +} + +TEST(Function, CanMoveConstructFromLambda) { + auto lambda = [] {}; + ftl::Function<void()> f{std::move(lambda)}; +} + +TEST(Function, TerseDeducedConstructAndAssignFromLambda) { + auto f = ftl::Function([] { return 1; }); + EXPECT_EQ(f(), 1); + + f = [] { return 2; }; + EXPECT_EQ(f(), 2); +} + +namespace { + +struct ImplicitConversionsHelper { + auto exact(int) -> int { return 0; } + auto inexact(long) -> short { return 0; } + // TODO: Switch to `auto templated(auto x)` with C++20 + template <typename T> + T templated(T x) { + return x; + } + + static auto static_exact(int) -> int { return 0; } + static auto static_inexact(long) -> short { return 0; } + // TODO: Switch to `static auto static_templated(auto x)` with C++20 + template <typename T> + static T static_templated(T x) { + return x; + } +}; + +} // namespace + +TEST(Function, ImplicitConversions) { + using Function = ftl::Function<int(int)>; + auto check = [](Function f) { return f(0); }; + auto exact = [](int) -> int { return 0; }; + auto inexact = [](long) -> short { return 0; }; + auto templated = [](auto x) { return x; }; + + ImplicitConversionsHelper helper; + + // Note, `check(nullptr)` would crash, so we can only check if it would be invocable. + static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>); + + // Note: We invoke each of these to fully expand all the templates involved. + EXPECT_EQ(check(ftl::no_op), 0); + + EXPECT_EQ(check(exact), 0); + EXPECT_EQ(check(inexact), 0); + EXPECT_EQ(check(templated), 0); + + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0); + + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0); +} + +TEST(Function, MakeWithNonConstMemberFunction) { + struct Observer { + bool called = false; + void setCalled() { called = true; } + } observer; + + auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithConstMemberFunction) { + struct Observer { + mutable bool called = false; + void setCalled() const { called = true; } + } observer; + + const auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithConstClassPointer) { + const struct Observer { + mutable bool called = false; + void setCalled() const { called = true; } + } observer; + + const auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithNonCapturingLambda) { + auto f = ftl::make_function([](int a, int b) { return a + b; }); + EXPECT_EQ(f(1, 2), 3); +} + +TEST(Function, MakeWithCapturingLambda) { + bool called = false; + auto f = ftl::make_function([&called](int a, int b) { + called = true; + return a + b; + }); + EXPECT_EQ(f(1, 2), 3); + EXPECT_TRUE(called); +} + +TEST(Function, MakeWithCapturingMutableLambda) { + bool called = false; + auto f = ftl::make_function([&called](int a, int b) mutable { + called = true; + return a + b; + }); + EXPECT_EQ(f(1, 2), 3); + EXPECT_TRUE(called); +} + +TEST(Function, MakeWithThreePointerCapturingLambda) { + bool my_bool = false; + int my_int = 0; + float my_float = 0.f; + + auto f = ftl::make_function( + [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable { + *ptr_bool = true; + *ptr_int = 1; + *ptr_float = 1.f; + + return a + b; + }); + + EXPECT_EQ(f(1, 2), 3); + + EXPECT_TRUE(my_bool); + EXPECT_EQ(my_int, 1); + EXPECT_EQ(my_float, 1.f); +} + +TEST(Function, MakeWithFreeFunction) { + auto f = ftl::make_function<&std::make_unique<int, int>>(); + std::unique_ptr<int> unique_int = f(1); + ASSERT_TRUE(unique_int); + EXPECT_EQ(*unique_int, 1); +} + +TEST(Function, CopyToLarger) { + int counter = 0; + ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }}; + ftl::Function<void(), 1> b = a; + ftl::Function<void(), 2> c = a; + + EXPECT_EQ(counter, 0); + a(); + EXPECT_EQ(counter, 1); + b(); + EXPECT_EQ(counter, 2); + c(); + EXPECT_EQ(counter, 3); + + b = [ptr_counter = &counter] { (*ptr_counter) += 2; }; + c = [ptr_counter = &counter] { (*ptr_counter) += 3; }; + + b(); + EXPECT_EQ(counter, 5); + c(); + EXPECT_EQ(counter, 8); +} + +} // namespace android::test diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 44a9e52d5b..eb4d3df21d 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -423,7 +423,6 @@ cc_defaults { "libhidlbase", "liblog", "libnativewindow", - "libselinux", "libsync", "libui", "libutils", diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 744201a5df..11f5174d76 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -38,43 +38,10 @@ #include <private/gui/BufferQueueThreadState.h> #if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER) #include <binder/PermissionCache.h> -#include <selinux/android.h> -#include <selinux/selinux.h> #endif #include <system/window.h> -namespace { -#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER) -int selinux_log_suppress_callback(int, const char*, ...) { // NOLINT - // DO NOTHING - return 0; -} - -bool hasAccessToPermissionService() { - char* ctx; - - if (getcon(&ctx) == -1) { - // Failed to get current selinux context - return false; - } - - union selinux_callback cb; - - cb.func_log = selinux_log_suppress_callback; - selinux_set_callback(SELINUX_CB_LOG, cb); - - bool hasAccess = selinux_check_access(ctx, "u:object_r:permission_service:s0", - "service_manager", "find", NULL) == 0; - freecon(ctx); - cb.func_log = hasAccess ? selinux_log_callback : selinux_vendor_log_callback; - selinux_set_callback(SELINUX_CB_LOG, cb); - - return hasAccess; -} -#endif -} // namespace - namespace android { // Macros for include BufferQueueCore information in log messages @@ -843,18 +810,14 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul const uid_t uid = BufferQueueThreadState::getCallingUid(); #if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER) // permission check can't be done for vendors as vendors have no access to - // the PermissionController. We need to do a runtime check as well, since - // the system variant of libgui can be loaded in a vendor process. For eg: - // if a HAL uses an llndk library that depends on libgui (libmediandk etc). - if (hasAccessToPermissionService()) { - const pid_t pid = BufferQueueThreadState::getCallingPid(); - if ((uid != shellUid) && - !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { - outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " - "from pid=%d, uid=%d\n", - pid, uid); - denied = true; - } + // the PermissionController. + const pid_t pid = BufferQueueThreadState::getCallingPid(); + if ((uid != shellUid) && + !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { + outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " + "from pid=%d, uid=%d\n", + pid, uid); + denied = true; } #else if (uid != shellUid) { diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 93df12471d..c6f33639c7 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -344,6 +344,10 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { handleRefreshRateUpdates(); } +void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId, int32_t, int32_t) { + LOG_ALWAYS_FATAL("dispatchHdcpLevelsChanged was called but was never registered"); +} + void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 5dd058cf9f..f3de96d2cd 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -195,6 +195,11 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, std::move(mFrameRateOverrides)); break; + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + dispatchHdcpLevelsChanged(ev.header.displayId, + ev.hdcpLevelsChange.connectedLevel, + ev.hdcpLevelsChange.maxLevel); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 8b6f2023dc..8d18551b69 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3117,7 +3117,6 @@ status_t SurfaceComposerClient::removeWindowInfosListener( ->removeWindowInfosListener(windowInfosListener, ComposerServiceAIDL::getComposerService()); } - // ---------------------------------------------------------------------------- status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 9933680c4b..c952ba2e10 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -207,6 +207,7 @@ public: MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId)); MOCK_METHOD3(dispatchFrameRateOverrides, void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>)); + MOCK_METHOD3(dispatchHdcpLevelsChanged, void(PhysicalDisplayId, int32_t, int32_t)); }; } // namespace android diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 9fef512b64..55a7aa7ddc 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -116,6 +116,8 @@ private: void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; + void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) override; void scheduleCallbacks(); diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index fe2dd206ed..82cd50c7bd 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -65,6 +65,9 @@ private: virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) = 0; + virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) = 0; + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 79582ce685..8c1103bfc2 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -58,7 +58,6 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: - enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), @@ -66,6 +65,7 @@ public: DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), + DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'), }; struct Event { @@ -101,12 +101,22 @@ public: float frameRateHz __attribute__((aligned(8))); }; + /* + * The values are defined in aidl: + * hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl + */ + struct HdcpLevelsChange { + int32_t connectedLevel; + int32_t maxLevel; + }; + Header header; union { VSync vsync; Hotplug hotplug; ModeChange modeChange; FrameRateOverride frameRateOverride; + HdcpLevelsChange hdcpLevelsChange; }; }; diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 4d4c5e4394..b72b71a3ce 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -176,6 +176,8 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), CLONE = static_cast<uint32_t>(os::InputConfig::CLONE), + GLOBAL_STYLUS_BLOCKS_TOUCH = + static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH), // clang-format on }; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bd5b67b1d0..8eaff00cbd 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -379,6 +379,11 @@ std::ostream& operator<<(std::ostream& out, const KeyEvent& event) { return out; } +std::ostream& operator<<(std::ostream& out, const PointerProperties& properties) { + out << "Pointer(id=" << properties.id << ", " << ftl::enum_string(properties.toolType) << ")"; + return out; +} + // --- PointerCoords --- float PointerCoords::getAxisValue(int32_t axis) const { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 09e98d0515..669f801764 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -646,7 +646,7 @@ status_t InputPublisher::publishMotionEvent( "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32 " \n%s", + "pointerCount=%" PRIu32 "\n%s", mChannel->getName().c_str(), __func__, seq, eventId, deviceId, inputEventSourceToString(source).c_str(), displayId, MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags, diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 4e644fff06..5d391551c2 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -150,4 +150,11 @@ enum InputConfig { * likely a duplicate window with the same client token, but different bounds. */ CLONE = 1 << 16, + + /** + * If the stylus is currently down *anywhere* on the screen, new touches will not be delivered + * to the window with this flag. This helps prevent unexpected clicks on some system windows, + * like StatusBar and TaskBar. + */ + GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17, } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 54eeb39935..11f69941bd 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -52,7 +52,14 @@ flag { flag { name: "enable_touchpad_typing_palm_rejection" namespace: "input" - description: "Enable additional palm rejection on touchpad while typing" + description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard" + bug: "301055381" +} + +flag { + name: "enable_v2_touchpad_typing_palm_rejection" + namespace: "input" + description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click." bug: "301055381" } diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index e7b2195056..745b6f30cc 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -113,6 +113,22 @@ static_assert( AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, "HAL and AHardwareBuffer pixel format don't match"); +static enum AHardwareBufferStatus filterStatus(status_t status) { + switch (status) { + case STATUS_OK: + return AHARDWAREBUFFER_STATUS_OK; + case STATUS_NO_MEMORY: + return AHARDWAREBUFFER_STATUS_NO_MEMORY; + case STATUS_BAD_VALUE: + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + case STATUS_UNKNOWN_TRANSACTION: + case STATUS_INVALID_OPERATION: + return AHARDWAREBUFFER_STATUS_UNSUPPORTED; + default: + return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR; + } +} + // ---------------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------------- @@ -511,6 +527,24 @@ binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull bu return AParcel_viewPlatformParcel(parcel)->write(*gb); } +ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return ADATASPACE_UNKNOWN; + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + status_t status = gb->getDataspace(&dataspace); + if (status != OK) { + return ADATASPACE_UNKNOWN; + } + return static_cast<ADataSpace>(dataspace); +} + +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer, + ADataSpace dataspace) { + GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + auto& mapper = GraphicBufferMapper::get(); + return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace))); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -552,6 +586,56 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, return NO_ERROR; } +enum AHardwareBufferStatus AHardwareBuffer_allocate2( + const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions, + size_t additionalOptionsSize, AHardwareBuffer** outBuffer) { + (void)additionalOptions; + (void)additionalOptionsSize; + if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) { + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + } + + int format = AHardwareBuffer_convertToPixelFormat(desc->format); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + + std::vector<GraphicBufferAllocator::AdditionalOptions> extras; + extras.reserve(additionalOptionsSize); + for (size_t i = 0; i < additionalOptionsSize; i++) { + extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name, + additionalOptions[i].value}); + } + + const auto extrasCount = extras.size(); + auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = true, + .width = desc->width, + .height = desc->height, + .format = format, + .layerCount = desc->layers, + .usage = usage, + .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]", + .extras = std::move(extras), + }); + + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == nullptr) { + if (err == NO_MEMORY) { + GraphicBuffer::dumpAllocationsToSystemLog(); + } + ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p", + desc->width, desc->height, desc->layers, extrasCount, strerror(-err), + gbuffer->handle); + return filterStatus(err == 0 ? UNKNOWN_ERROR : err); + } + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return AHARDWAREBUFFER_STATUS_OK; +} + // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- @@ -652,12 +736,9 @@ uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } +// TODO: Remove, this is just to make an overly aggressive ABI checker happy int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) { - GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); - auto& mapper = GraphicBufferMapper::get(); - ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; - mapper.getDataspace(gb->handle, &dataspace); - return static_cast<int32_t>(dataspace); + return ::AHardwareBuffer_getDataSpace(buffer); } uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index dd5958de28..f97eed5db3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -152,31 +152,56 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); - static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); - static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); - static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); - static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); - static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); - static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); - static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); - static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); - static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); - static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); - static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); - static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); - static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); - static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); - static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); - static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK)); - static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); - static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL)); - static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); - static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) == + static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) == + static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) == + static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) == + static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) == + static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) == + static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) == + static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) == + static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) == + static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); + static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) == + static_cast<int>(HAL_DATASPACE_RANGE_MASK)); + static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) == + static_cast<int>(HAL_DATASPACE_RANGE_FULL)); + static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) == + static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) == + static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 880c694934..f145a2f7c2 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -27,8 +27,8 @@ #include <stdint.h> -struct AHardwareBuffer; -struct AHardwareBuffer_Desc; +#include <vndk/hardware_buffer.h> + struct ANativeWindowBuffer; namespace android { @@ -46,11 +46,6 @@ uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); // convert HAL format to AHardwareBuffer format (note: this is a no-op) uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); -// retrieves a dataspace from the AHardwareBuffer metadata, if the device -// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not -// supported. -int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer); - // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index eab21fbdf1..d4278bedf3 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -37,7 +37,7 @@ __BEGIN_DECLS /** * ADataSpace. */ -enum ADataSpace { +enum ADataSpace : int32_t { /** * Default-assumption data space, when not explicitly specified. * @@ -63,7 +63,7 @@ enum ADataSpace { * Defines the chromaticity coordinates of the source primaries in terms of * the CIE 1931 definition of x and y specified in ISO 11664-1. */ - STANDARD_MASK = 63 << 16, + ADATASPACE_STANDARD_MASK = 63 << 16, /** * Chromacity coordinates are unknown or are determined by the application. @@ -78,7 +78,7 @@ enum ADataSpace { * For all other formats standard is undefined, and implementations should use * an appropriate standard for the data represented. */ - STANDARD_UNSPECIFIED = 0 << 16, + ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16, /** * <pre> @@ -91,7 +91,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation * for RGB conversion. */ - STANDARD_BT709 = 1 << 16, + ADATASPACE_STANDARD_BT709 = 1 << 16, /** * <pre> @@ -106,7 +106,7 @@ enum ADataSpace { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_625 = 2 << 16, + ADATASPACE_STANDARD_BT601_625 = 2 << 16, /** * <pre> @@ -119,7 +119,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation * for RGB conversion. */ - STANDARD_BT601_625_UNADJUSTED = 3 << 16, + ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16, /** * <pre> @@ -134,7 +134,7 @@ enum ADataSpace { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_525 = 4 << 16, + ADATASPACE_STANDARD_BT601_525 = 4 << 16, /** * <pre> @@ -147,7 +147,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation * for RGB conversion (as in SMPTE 240M). */ - STANDARD_BT601_525_UNADJUSTED = 5 << 16, + ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16, /** * <pre> @@ -160,7 +160,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion. */ - STANDARD_BT2020 = 6 << 16, + ADATASPACE_STANDARD_BT2020 = 6 << 16, /** * <pre> @@ -173,7 +173,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion using the linear domain. */ - STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, + ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, /** * <pre> @@ -186,7 +186,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation * for RGB conversion. */ - STANDARD_BT470M = 8 << 16, + ADATASPACE_STANDARD_BT470M = 8 << 16, /** * <pre> @@ -199,7 +199,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation * for RGB conversion. */ - STANDARD_FILM = 9 << 16, + ADATASPACE_STANDARD_FILM = 9 << 16, /** * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) @@ -210,7 +210,7 @@ enum ADataSpace { * red 0.680 0.320 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_DCI_P3 = 10 << 16, + ADATASPACE_STANDARD_DCI_P3 = 10 << 16, /** * Adobe RGB @@ -221,7 +221,7 @@ enum ADataSpace { * red 0.640 0.330 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_ADOBE_RGB = 11 << 16, + ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16, /** * Transfer aspect @@ -236,7 +236,7 @@ enum ADataSpace { * component. Implementation may apply the transfer function in RGB space * for all pixel formats if desired. */ - TRANSFER_MASK = 31 << 22, + ADATASPACE_TRANSFER_MASK = 31 << 22, /** * Transfer characteristics are unknown or are determined by the @@ -244,13 +244,13 @@ enum ADataSpace { * * Implementations should use the following transfer functions: * - * For YCbCr formats: use TRANSFER_SMPTE_170M - * For RGB formats: use TRANSFER_SRGB + * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M + * For RGB formats: use ADATASPACE_TRANSFER_SRGB * * For all other formats transfer function is undefined, and implementations * should use an appropriate standard for the data represented. */ - TRANSFER_UNSPECIFIED = 0 << 22, + ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22, /** * Linear transfer. @@ -260,7 +260,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_LINEAR = 1 << 22, + ADATASPACE_TRANSFER_LINEAR = 1 << 22, /** * sRGB transfer. @@ -271,7 +271,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SRGB = 2 << 22, + ADATASPACE_TRANSFER_SRGB = 2 << 22, /** * SMPTE 170M transfer. @@ -282,7 +282,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SMPTE_170M = 3 << 22, + ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22, /** * Display gamma 2.2. @@ -292,7 +292,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_2 = 4 << 22, + ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22, /** * Display gamma 2.6. @@ -302,7 +302,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_6 = 5 << 22, + ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22, /** * Display gamma 2.8. @@ -312,7 +312,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_8 = 6 << 22, + ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22, /** * SMPTE ST 2084 (Dolby Perceptual Quantizer). @@ -328,7 +328,7 @@ enum ADataSpace { * L = 1 corresponds to 10000 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_ST2084 = 7 << 22, + ADATASPACE_TRANSFER_ST2084 = 7 << 22, /** * ARIB STD-B67 Hybrid Log Gamma. @@ -344,7 +344,7 @@ enum ADataSpace { * to reference white level of 100 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_HLG = 8 << 22, + ADATASPACE_TRANSFER_HLG = 8 << 22, /** * Range aspect @@ -352,7 +352,7 @@ enum ADataSpace { * Defines the range of values corresponding to the unit range of 0-1. * This is defined for YCbCr only, but can be expanded to RGB space. */ - RANGE_MASK = 7 << 27, + ADATASPACE_RANGE_MASK = 7 << 27, /** * Range is unknown or are determined by the application. Implementations @@ -365,13 +365,13 @@ enum ADataSpace { * For all other formats range is undefined, and implementations should use * an appropriate range for the data represented. */ - RANGE_UNSPECIFIED = 0 << 27, + ADATASPACE_RANGE_UNSPECIFIED = 0 << 27, /** * Full range uses all values for Y, Cb and Cr from * 0 to 2^b-1, where b is the bit depth of the color format. */ - RANGE_FULL = 1 << 27, + ADATASPACE_RANGE_FULL = 1 << 27, /** * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and @@ -386,7 +386,7 @@ enum ADataSpace { * Luma (Y) samples should range from 64 to 940, inclusive * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive */ - RANGE_LIMITED = 2 << 27, + ADATASPACE_RANGE_LIMITED = 2 << 27, /** * Extended range is used for scRGB. Intended for use with @@ -395,7 +395,7 @@ enum ADataSpace { * color outside the sRGB gamut. * Used to blend / merge multiple dataspaces on a single display. */ - RANGE_EXTENDED = 3 << 27, + ADATASPACE_RANGE_EXTENDED = 3 << 27, /** * scRGB linear encoding @@ -410,7 +410,8 @@ enum ADataSpace { * * Uses extended range, linear transfer and BT.709 standard. */ - ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED + ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_EXTENDED /** * sRGB gamma encoding @@ -425,7 +426,8 @@ enum ADataSpace { * * Uses full range, sRGB transfer BT.709 standard. */ - ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * scRGB @@ -440,14 +442,16 @@ enum ADataSpace { * * Uses extended range, sRGB transfer and BT.709 standard. */ - ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED + ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_EXTENDED /** * Display P3 * * Uses full range, sRGB transfer and D65 DCI-P3 standard. */ - ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -456,7 +460,8 @@ enum ADataSpace { * * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL + ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -465,7 +470,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 + // | ADATASPACE_RANGE_LIMITED /** * Adobe RGB @@ -475,7 +481,8 @@ enum ADataSpace { * Note: Application is responsible for gamma encoding the data as * a 2.2 gamma encoding is not supported in HW. */ - ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL + ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB | + // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL /** * JPEG File Interchange Format (JFIF) @@ -484,7 +491,8 @@ enum ADataSpace { * * Uses full range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 601 (BT.601) - 625-line @@ -493,7 +501,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 601 (BT.601) - 525-line @@ -502,7 +511,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.601_525 standard. */ - ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 2020 (BT.2020) @@ -511,7 +521,8 @@ enum ADataSpace { * * Uses full range, SMPTE 170M transfer and BT2020 standard. */ - ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 709 (BT.709) @@ -520,7 +531,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.709 standard. */ - ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_LIMITED /** * SMPTE EG 432-1 and SMPTE RP 431-2 @@ -532,7 +544,8 @@ enum ADataSpace { * Note: Application is responsible for gamma encoding the data as * a 2.6 gamma encoding is not supported in HW. */ - ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL + ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 | + // ADATASPACE_RANGE_FULL /** * sRGB linear encoding @@ -546,21 +559,24 @@ enum ADataSpace { * * Uses full range, linear transfer and BT.709 standard. */ - ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_FULL /** * Hybrid Log Gamma encoding * * Uses full range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_FULL /** * ITU Hybrid Log Gamma encoding * * Uses limited range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED + ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_LIMITED /** * Depth @@ -574,7 +590,37 @@ enum ADataSpace { * * Embedded depth metadata following the dynamic depth specification. */ - ADATASPACE_DYNAMIC_DEPTH = 4098 + ADATASPACE_DYNAMIC_DEPTH = 4098, + +#ifndef ADATASPACE_SKIP_LEGACY_DEFINES + STANDARD_MASK = ADATASPACE_STANDARD_MASK, + STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED, + STANDARD_BT709 = ADATASPACE_STANDARD_BT709, + STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625, + STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED, + STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525, + STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED, + STANDARD_BT470M = ADATASPACE_STANDARD_BT470M, + STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020, + STANDARD_FILM = ADATASPACE_STANDARD_FILM, + STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3, + STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB, + TRANSFER_MASK = ADATASPACE_TRANSFER_MASK, + TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED, + TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR, + TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M, + TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2, + TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6, + TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8, + TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB, + TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084, + TRANSFER_HLG = ADATASPACE_TRANSFER_HLG, + RANGE_MASK = ADATASPACE_RANGE_MASK, + RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED, + RANGE_FULL = ADATASPACE_RANGE_FULL, + RANGE_LIMITED = ADATASPACE_RANGE_LIMITED, + RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED, +#endif }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 21798d0e29..e0e30c3283 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -46,6 +46,9 @@ #define ANDROID_HARDWARE_BUFFER_H #include <android/rect.h> +#define ADATASPACE_SKIP_LEGACY_DEFINES +#include <android/data_space.h> +#undef ADATASPACE_SKIP_LEGACY_DEFINES #include <inttypes.h> #include <sys/cdefs.h> diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h index 0d5727d0e6..68ac7e0b34 100644 --- a/libs/nativewindow/include/android/native_window_aidl.h +++ b/libs/nativewindow/include/android/native_window_aidl.h @@ -103,14 +103,22 @@ public: binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { reset(); - return ANativeWindow_readFromParcel(parcel, &mWindow); + if (__builtin_available(android __ANDROID_API_U__, *)) { + return ANativeWindow_readFromParcel(parcel, &mWindow); + } else { + return STATUS_FAILED_TRANSACTION; + } } binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { if (!mWindow) { return STATUS_BAD_VALUE; } - return ANativeWindow_writeToParcel(mWindow, parcel); + if (__builtin_available(android __ANDROID_API_U__, *)) { + return ANativeWindow_writeToParcel(mWindow, parcel); + } else { + return STATUS_FAILED_TRANSACTION; + } } /** diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 21931bb553..491860125f 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -21,6 +21,7 @@ #include <android/hardware_buffer.h> #include <cutils/native_handle.h> +#include <errno.h> __BEGIN_DECLS @@ -105,6 +106,76 @@ enum { AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16, }; +/** + * Additional options for AHardwareBuffer_allocate2. These correspond to + * android.hardware.graphics.common.ExtendableType + */ +typedef struct { + const char* _Nonnull name; + int64_t value; +} AHardwareBufferLongOptions; + +enum AHardwareBufferStatus : int32_t { + /* Success, no error */ + AHARDWAREBUFFER_STATUS_OK = 0, + /* There's insufficient memory to satisfy the request */ + AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM, + /* The given argument is invalid */ + AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL, + /* The requested operation is not supported by the device */ + AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS, + /* An unknown error occurred */ + AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1), +}; + +/** + * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options + * + * If allocation succeeds, the buffer can be used according to the + * usage flags specified in its description. If a buffer is used in ways + * not compatible with its usage flags, the results are undefined and + * may include program termination. + * + * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride` + * is ignored. + * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional + * string key + long value options that may be specified. May be null if + * `additionalOptionsSize` is 0 + * @param additionalOptionsSize The number of additional options to pass + * @param outBuffer The resulting buffer allocation + * @return AHARDWAREBUFFER_STATUS_OK on success + * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation + * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported + * by the device + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error + * any reason. The returned buffer has a reference count of 1. + */ +enum AHardwareBufferStatus AHardwareBuffer_allocate2( + const AHardwareBuffer_Desc* _Nonnull desc, + const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Queries the dataspace of the given AHardwareBuffer. + * + * @param buffer The non-null buffer for which to query the Dataspace + * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set + */ +enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the dataspace of the given AHardwareBuffer + * @param buffer The non-null buffer for which to set the dataspace + * @param dataSpace The dataspace to set + * @return AHARDWAREBUFFER_STATUS_OK on success, + * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace, + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure. + */ +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer, + enum ADataSpace dataSpace) + __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index dcb506815c..a185a59fca 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,6 +2,7 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; + AHardwareBuffer_allocate2; # llndk # systemapi AHardwareBuffer_createFromHandle; # llndk # systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 @@ -16,6 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 + AHardwareBuffer_getDataSpace; # llndk # systemapi + AHardwareBuffer_setDataSpace; # llndk # systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index ef863b6d67..1f0128a48e 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 +#include <android-base/properties.h> +#include <android/data_space.h> #include <android/hardware/graphics/common/1.0/types.h> #include <gtest/gtest.h> #include <private/android/AHardwareBufferHelpers.h> @@ -26,6 +28,10 @@ using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; +static bool IsCuttlefish() { + return ::android::base::GetProperty("ro.product.board", "") == "cutf"; +} + static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, uint64_t actual, const char* type) { std::ostringstream ss; @@ -170,3 +176,83 @@ TEST(AHardwareBufferTest, GetIdTest) { EXPECT_NE(id1, id2); } + +TEST(AHardwareBufferTest, Allocate2NoExtras) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, Allocate2WithExtras) { + if (!IsCuttlefish()) { + GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras"; + } + + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + std::array<AHardwareBufferLongOptions, 1> extras = {{ + {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, + }}; + ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, GetSetDataspace) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer)); + + EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer)); + AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3); + if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) { + EXPECT_EQ(0, status); + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + } + + AHardwareBuffer_release(buffer); +}
\ No newline at end of file diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 30737c1bf6..d7c7eb3153 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "device-tests", ], shared_libs: [ + "libbase", "libgui", "liblog", "libnativewindow", @@ -44,5 +45,8 @@ cc_test { "ANativeWindowTest.cpp", "c_compatibility.c", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 2053c6a34f..d688b51793 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -236,7 +236,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo err = selectEGLConfig(display, format, 0, &config); if (err != NO_ERROR) { // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up" + " (format: %d, vendor: %s, version: %s, extensions: %s, Client" + " API: %s)", + format, eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS), + eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); } } } diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index d112a1265c..f8ee3fc607 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -248,6 +248,22 @@ ssize_t SensorManager::getSensorList(Sensor const* const** list) { return static_cast<ssize_t>(mSensors.size()); } +ssize_t SensorManager::getDefaultDeviceSensorList(Vector<Sensor> & list) { + Mutex::Autolock _l(mLock); + status_t err = assertStateLocked(); + if (err < 0) { + return static_cast<ssize_t>(err); + } + + if (mDeviceId == DEVICE_ID_DEFAULT) { + list = mSensors; + } else { + list = mSensorServer->getSensorList(mOpPackageName); + } + + return static_cast<ssize_t>(list.size()); +} + ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index e67fac7617..49f050a0ac 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -58,6 +58,7 @@ public: ~SensorManager(); ssize_t getSensorList(Sensor const* const** list); + ssize_t getDefaultDeviceSensorList(Vector<Sensor> & list); ssize_t getDynamicSensorList(Vector<Sensor>& list); ssize_t getDynamicSensorList(Sensor const* const** list); ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list); diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index e9b5decee8..a5aca9912f 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -384,8 +384,8 @@ std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, bool importBuffers) const { + uint32_t* outStride, buffer_handle_t* outBufferHandles, + bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; @@ -400,6 +400,8 @@ status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 474d381dbb..152b35a505 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -371,7 +371,7 @@ std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); @@ -383,6 +383,8 @@ status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 03ff58a76c..d6970e0477 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -1069,7 +1069,7 @@ std::string Gralloc4Allocator::dumpDebugInfo(bool less) const { status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, @@ -1084,6 +1084,8 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, return error; } + constexpr auto bufferCount = 1; + if (mAidlAllocator) { AllocationResult result; #pragma clang diagnostic push diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 2ec6d18fda..b07e15534a 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -19,6 +19,7 @@ #include <ui/Gralloc5.h> +#include <aidl/android/hardware/graphics/allocator/AllocationError.h> #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> @@ -215,55 +216,75 @@ std::string Gralloc5Allocator::dumpDebugInfo(bool less) const { status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, bool importBuffers) const { - auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage); + uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers) const { + auto result = allocate(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = importBuffers, + .width = width, + .height = height, + .format = format, + .layerCount = layerCount, + .usage = usage, + .requestorName = requestorName, + }); + + *outStride = result.stride; + outBufferHandles[0] = result.handle; + return result.status; +} + +GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate( + const GraphicBufferAllocator::AllocationRequest& request) const { + auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage); if (!descriptorInfo) { - return BAD_VALUE; + return GraphicBufferAllocator::AllocationResult{BAD_VALUE}; + } + + descriptorInfo->additionalOptions.reserve(request.extras.size()); + for (const auto& option : request.extras) { + ExtendableType type; + type.name = option.name; + type.value = option.value; + descriptorInfo->additionalOptions.push_back(std::move(type)); } AllocationResult result; - auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result); + auto status = mAllocator->allocate2(*descriptorInfo, 1, &result); if (!status.isOk()) { auto error = status.getExceptionCode(); if (error == EX_SERVICE_SPECIFIC) { - error = status.getServiceSpecificError(); - } - if (error == OK) { - error = UNKNOWN_ERROR; + switch (static_cast<AllocationError>(status.getServiceSpecificError())) { + case AllocationError::BAD_DESCRIPTOR: + error = BAD_VALUE; + break; + case AllocationError::NO_RESOURCES: + error = NO_MEMORY; + break; + default: + error = UNKNOWN_ERROR; + break; + } } - return error; + return GraphicBufferAllocator::AllocationResult{error}; } - if (importBuffers) { - for (uint32_t i = 0; i < bufferCount; i++) { - auto handle = makeFromAidl(result.buffers[i]); - auto error = mMapper.importBuffer(handle, &outBufferHandles[i]); - native_handle_delete(handle); - if (error != NO_ERROR) { - for (uint32_t j = 0; j < i; j++) { - mMapper.freeBuffer(outBufferHandles[j]); - outBufferHandles[j] = nullptr; - } - return error; - } + GraphicBufferAllocator::AllocationResult ret{OK}; + if (request.importBuffer) { + auto handle = makeFromAidl(result.buffers[0]); + auto error = mMapper.importBuffer(handle, &ret.handle); + native_handle_delete(handle); + if (error != NO_ERROR) { + return GraphicBufferAllocator::AllocationResult{error}; } } else { - for (uint32_t i = 0; i < bufferCount; i++) { - outBufferHandles[i] = dupFromAidl(result.buffers[i]); - if (!outBufferHandles[i]) { - for (uint32_t j = 0; j < i; j++) { - auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]); - native_handle_close(buffer); - native_handle_delete(buffer); - outBufferHandles[j] = nullptr; - } - return NO_MEMORY; - } + ret.handle = dupFromAidl(result.buffers[0]); + if (!ret.handle) { + return GraphicBufferAllocator::AllocationResult{NO_MEMORY}; } } - *outStride = result.stride; + ret.stride = result.stride; // Release all the resources held by AllocationResult (specifically any remaining FDs) result = {}; @@ -272,7 +293,7 @@ status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct) // IPCThreadState::self()->flushCommands(); - return OK; + return ret; } void Gralloc5Mapper::preload() { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 429760ffe0..c007fdb587 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -106,6 +106,26 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m inUsage, inStride); } +GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request) + : GraphicBuffer() { + GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); + auto result = allocator.allocate(request); + mInitCheck = result.status; + if (result.status == NO_ERROR) { + handle = result.handle; + stride = result.stride; + + mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); + + width = static_cast<int>(request.width); + height = static_cast<int>(request.height); + format = request.format; + layerCount = request.layerCount; + usage = request.usage; + usage_deprecated = int(usage); + } +} + GraphicBuffer::~GraphicBuffer() { ATRACE_CALL(); @@ -143,6 +163,10 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const const_cast<GraphicBuffer*>(this)); } +status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const { + return mBufferMapper.getDataspace(handle, outDataspace); +} + status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eb0bd4ed0a..98082fb81e 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -113,6 +113,79 @@ void GraphicBufferAllocator::dumpToSystemLog(bool less) { ALOGD("%s", s.c_str()); } +auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult { + ATRACE_CALL(); + if (!request.width || !request.height) { + return AllocationResult(BAD_VALUE); + } + + const auto width = request.width; + const auto height = request.height; + + const uint32_t bpp = bytesPerPixel(request.format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + request.width, request.height, request.layerCount, request.format, request.usage); + return AllocationResult(BAD_VALUE); + } + + if (request.layerCount < 1) { + return AllocationResult(BAD_VALUE); + } + + auto result = mAllocator->allocate(request); + if (result.status == UNKNOWN_TRANSACTION) { + if (!request.extras.empty()) { + ALOGE("Failed to allocate with additional options, allocator version mis-match? " + "gralloc version = %d", + (int)mMapper.getMapperVersion()); + return result; + } + // If there's no additional options, fall back to previous allocate version + result.status = mAllocator->allocate(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage, + &result.stride, &result.handle, request.importBuffer); + } + + if (result.status != NO_ERROR) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": %d", + request.width, request.height, request.layerCount, request.format, request.usage, + result.status); + return result; + } + + if (!request.importBuffer) { + return result; + } + size_t bufSize; + + // if stride has no meaning or is too large, + // approximate size with the input width instead + if ((result.stride) != 0 && + std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) { + bufSize = static_cast<size_t>(width) * height * bpp; + } else { + bufSize = static_cast<size_t>((result.stride)) * height * bpp; + } + + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.width = width; + rec.height = height; + rec.stride = result.stride; + rec.format = request.format; + rec.layerCount = request.layerCount; + rec.usage = request.usage; + rec.size = bufSize; + rec.requestorName = request.requestorName; + list.add(result.handle, rec); + + return result; +} + status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, @@ -141,7 +214,7 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage, - 1, stride, handle, importBuffer); + stride, handle, importBuffer); if (error != NO_ERROR) { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": %d", diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 496ba57789..e6015e0b5e 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -23,6 +23,7 @@ #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/StrongPointer.h> +#include "GraphicBufferAllocator.h" #include <string> @@ -218,9 +219,13 @@ public: */ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers = true) const = 0; + + virtual GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const { + return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION); + } }; } // namespace android diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index a7b6f49206..e50bb3af3e 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -81,9 +81,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc2Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 7367549964..035684abcd 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -82,9 +82,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc3Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index df43be87cd..0f469c0b7e 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -174,9 +174,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc4Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h index 44b97d1a6f..f9e8f5e9fd 100644 --- a/libs/ui/include/ui/Gralloc5.h +++ b/libs/ui/include/ui/Gralloc5.h @@ -172,10 +172,12 @@ public: [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const override; + [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const override; + private: const Gralloc5Mapper &mMapper; std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index f859848b0c..652d8ba709 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -26,6 +26,7 @@ #include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> +#include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> @@ -103,6 +104,8 @@ public: uint32_t inLayerCount, uint64_t inUsage, std::string requestorName = "<Unknown>"); + GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&); + // Create a GraphicBuffer from an existing handle. enum HandleWrapMethod : uint8_t { // Wrap and use the handle directly. It assumes the handle has been @@ -169,6 +172,8 @@ public: mGenerationNumber = generation; } + status_t getDataspace(ui::Dataspace* outDataspace) const; + // This function is privileged. It requires access to the allocator // device or service, which usually involves adding suitable selinux // rules. diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 3ed988c4c2..8f461e193b 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -22,6 +22,7 @@ #include <memory> #include <string> +#include <vector> #include <cutils/native_handle.h> @@ -42,6 +43,35 @@ class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> public: static inline GraphicBufferAllocator& get() { return getInstance(); } + struct AdditionalOptions { + const char* name; + int64_t value; + }; + + struct AllocationRequest { + bool importBuffer; + uint32_t width; + uint32_t height; + PixelFormat format; + uint32_t layerCount; + uint64_t usage; + std::string requestorName; + std::vector<AdditionalOptions> extras; + }; + + struct AllocationResult { + status_t status; + buffer_handle_t handle = nullptr; + uint32_t stride = 0; + + explicit AllocationResult(status_t status) : status(status) {} + + explicit AllocationResult(buffer_handle_t handle, uint32_t stride) + : status(OK), handle(handle), stride(stride) {} + }; + + AllocationResult allocate(const AllocationRequest&); + /** * Allocates and imports a gralloc buffer. * diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp index f4c0afa71b..efca083e6e 100644 --- a/libs/ui/tests/GraphicBufferAllocator_test.cpp +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -51,7 +51,7 @@ public: std::cout << "Setting expected stride to " << stride << std::endl; EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())), allocate) - .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err))); + .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); } std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; } }; diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h index d62e3e2192..d02b3873e0 100644 --- a/libs/ui/tests/mock/MockGrallocAllocator.h +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -35,7 +35,7 @@ public: MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override)); MOCK_METHOD(status_t, allocate, (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint32_t layerCount, uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool less), (const, override)); }; diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h index bf6189d7af..654c903db2 100644 --- a/services/batteryservice/include/batteryservice/BatteryService.h +++ b/services/batteryservice/include/batteryservice/BatteryService.h @@ -38,6 +38,7 @@ enum { BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH + BATTERY_PROP_PART_STATUS = 12, // equals BATTERY_PROPERTY_PART_STATUS }; struct BatteryProperties { diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 45c9b5cf0b..69f42bc800 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -77,6 +77,7 @@ filegroup { "InputCommonConverter.cpp", "InputDeviceMetricsCollector.cpp", "InputFilter.cpp", + "InputFilterCallbacks.cpp", "InputProcessor.cpp", "PointerChoreographer.cpp", "PreferStylusOverTouchBlocker.cpp", diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 9c4a3eb274..5d87d34adb 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -44,31 +44,9 @@ AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) { return event; } -NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { - return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, - static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, - static_cast<int32_t>(event.action), event.flags, event.keyCode, - event.scanCode, event.metaState, event.downTime); -} - -namespace { - -class RustCallbacks : public IInputFilter::BnInputFilterCallbacks { -public: - RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {} - ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override { - mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); - return ndk::ScopedAStatus::ok(); - } - -private: - InputListenerInterface& mNextListener; -}; - -} // namespace - InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust) - : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) { + : mNextListener(listener), + mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) { LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk()); LOG_ALWAYS_FATAL_IF(!mInputFilterRust); } @@ -92,11 +70,11 @@ void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArg } void InputFilter::notifyKey(const NotifyKeyArgs& args) { - if (!isFilterEnabled()) { - mNextListener.notifyKey(args); + if (isFilterEnabled()) { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); return; } - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); + mNextListener.notifyKey(args); } void InputFilter::notifyMotion(const NotifyMotionArgs& args) { @@ -138,6 +116,15 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) { } } +void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { + std::scoped_lock _l(mLock); + + if (mConfig.stickyKeysEnabled != enabled) { + mConfig.stickyKeysEnabled = enabled; + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + } +} + void InputFilter::dump(std::string& dump) { dump += "InputFilter:\n"; } diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 06f7d0e601..9fa7a8758f 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -18,6 +18,7 @@ #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <utils/Mutex.h> +#include "InputFilterCallbacks.h" #include "InputListener.h" #include "NotifyArgs.h" @@ -33,6 +34,7 @@ public: */ virtual void dump(std::string& dump) = 0; virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0; + virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0; }; class InputFilter : public InputFilterInterface { @@ -56,11 +58,12 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override; + void setAccessibilityStickyKeysEnabled(bool enabled) override; void dump(std::string& dump) override; private: InputListenerInterface& mNextListener; - std::shared_ptr<IInputFilterCallbacks> mCallbacks; + std::shared_ptr<InputFilterCallbacks> mCallbacks; std::shared_ptr<IInputFilter> mInputFilterRust; mutable std::mutex mLock; InputFilterConfiguration mConfig GUARDED_BY(mLock); diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp new file mode 100644 index 0000000000..8c8f5e8f71 --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2023 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. + */ + +#define LOG_TAG "InputFilterCallbacks" + +#include "InputFilterCallbacks.h" + +namespace android { + +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; + +NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { + return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, + static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, + static_cast<int32_t>(event.action), event.flags, event.keyCode, + event.scanCode, event.metaState, event.downTime); +} + +InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener) + : mNextListener(listener) {} + +ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) { + mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) { + std::scoped_lock _l(mLock); + mStickyModifierState.modifierState = modifierState; + mStickyModifierState.lockedModifierState = lockedModifierState; + ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d", + modifierState, lockedModifierState); + return ndk::ScopedAStatus::ok(); +} + +uint32_t InputFilterCallbacks::getModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.modifierState; +} + +uint32_t InputFilterCallbacks::getLockedModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.lockedModifierState; +} + +} // namespace android diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h new file mode 100644 index 0000000000..c0a80fb6dc --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.h @@ -0,0 +1,55 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> +#include <android/binder_auto_utils.h> +#include <utils/Mutex.h> +#include <mutex> +#include "InputListener.h" +#include "NotifyArgs.h" + +/** + * The C++ component of InputFilter designed as a wrapper around the rust callback implementation. + */ +namespace android { + +using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter; +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; + +class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks { +public: + explicit InputFilterCallbacks(InputListenerInterface& listener); + ~InputFilterCallbacks() override = default; + + uint32_t getModifierState(); + uint32_t getLockedModifierState(); + +private: + InputListenerInterface& mNextListener; + mutable std::mutex mLock; + struct StickyModifierState { + uint32_t modifierState; + uint32_t lockedModifierState; + } mStickyModifierState GUARDED_BY(mLock); + + ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override; + ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) override; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 07a50411fb..0be4c327b7 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -327,14 +327,23 @@ void PointerChoreographer::updatePointerControllersLocked() { std::set<DeviceId> touchDevicesToKeep; std::set<DeviceId> stylusDevicesToKeep; - // Mark the displayIds or deviceIds of PointerControllers currently needed. + // Mark the displayIds or deviceIds of PointerControllers currently needed, and create + // new PointerControllers if necessary. for (const auto& info : mInputDeviceInfos) { const uint32_t sources = info.getSources(); if (isFromSource(sources, AINPUT_SOURCE_MOUSE) || isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) { - const int32_t resolvedDisplayId = - getTargetMouseDisplayLocked(info.getAssociatedDisplayId()); - mouseDisplaysToKeep.insert(resolvedDisplayId); + const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId()); + mouseDisplaysToKeep.insert(displayId); + // For mice, show the cursor immediately when the device is first connected or + // when it moves to a new display. + auto [mousePointerIt, isNewMousePointer] = + mMousePointersByDisplay.try_emplace(displayId, + getMouseControllerConstructor(displayId)); + auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId()); + if (isNewMouseDevice || isNewMousePointer) { + mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE); + } } if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled && info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) { @@ -356,6 +365,11 @@ void PointerChoreographer::updatePointerControllersLocked() { std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) { return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end(); }); + std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) { + return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(), + [id](const auto& info) { return info.getId() == id; }) == + mInputDeviceInfos.end(); + }); // Notify the policy if there's a change on the pointer display ID. notifyPointerDisplayIdChangedLocked(); diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index 9b809a17ff..f46419ec2e 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -139,6 +139,7 @@ private: int32_t mDefaultMouseDisplayId GUARDED_BY(mLock); int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock); std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock); + std::set<DeviceId> mMouseDevices GUARDED_BY(mLock); std::vector<DisplayViewport> mViewports GUARDED_BY(mLock); bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 14b41cd00c..2921d30b22 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -33,6 +33,9 @@ interface IInputFilter { interface IInputFilterCallbacks { /** Sends back a filtered key event */ void sendKeyEvent(in KeyEvent event); + + /** Sends back modifier state */ + void onModifierStateChanged(int modifierState, int lockedModifierState); } /** Returns if InputFilter is enabled */ diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl index 3b2e88ba24..38b161203b 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl @@ -22,4 +22,6 @@ package com.android.server.inputflinger; parcelable InputFilterConfiguration { // Threshold value for Bounce keys filter (check bounce_keys_filter.rs) long bounceKeysThresholdNs; + // If sticky keys filter is enabled + boolean stickyKeysEnabled; }
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 6033398b25..112f16b38a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -72,7 +72,8 @@ using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; namespace input_flags = com::android::input::flags; -static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops(); +// TODO(b/312714754): remove the corresponding code, as well. +static const bool REMOVE_APP_SWITCH_DROPS = true; namespace android::inputdispatcher { @@ -252,6 +253,14 @@ Result<void> validateInputEvent(const InputEvent& event) { } } +std::bitset<MAX_POINTER_ID + 1> getPointerIds(const std::vector<PointerProperties>& pointers) { + std::bitset<MAX_POINTER_ID + 1> pointerIds; + for (const PointerProperties& pointer : pointers) { + pointerIds.set(pointer.id); + } + return pointerIds; +} + std::string dumpRegion(const Region& region) { if (region.isEmpty()) { return "<empty>"; @@ -630,15 +639,15 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } // We should consider all hovering pointers here. But for now, just use the first one - const int32_t pointerId = entry.pointerProperties[0].id; + const PointerProperties& pointer = entry.pointerProperties[0]; std::set<sp<WindowInfoHandle>> oldWindows; if (oldState != nullptr) { - oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId); + oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointer.id); } std::set<sp<WindowInfoHandle>> newWindows = - newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointerId); + newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointer.id); // If the pointer is no longer in the new window set, send HOVER_EXIT. for (const sp<WindowInfoHandle>& oldWindow : oldWindows) { @@ -672,7 +681,7 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS; } - touchedWindow.addHoveringPointer(entry.deviceId, pointerId); + touchedWindow.addHoveringPointer(entry.deviceId, pointer); if (canReceiveForegroundTouches(*newWindow->getInfo())) { touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND; } @@ -740,6 +749,20 @@ bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) { return true; } +/** + * Return true if stylus is currently down anywhere on the specified display, and false otherwise. + */ +bool isStylusActiveInDisplay( + int32_t displayId, + const std::unordered_map<int32_t /*displayId*/, TouchState>& touchStatesByDisplay) { + const auto it = touchStatesByDisplay.find(displayId); + if (it == touchStatesByDisplay.end()) { + return false; + } + const TouchState& state = it->second; + return state.hasActiveStylus(); +} + } // namespace // --- InputDispatcher --- @@ -2135,7 +2158,6 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, InputEventInjectionResult& outInjectionResult) { - std::string reason; outInjectionResult = InputEventInjectionResult::FAILED; // Default result int32_t displayId = getTargetDisplayId(entry); @@ -2320,7 +2342,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ const auto [x, y] = resolveTouchedPosition(entry); const int32_t pointerIndex = MotionEvent::getActionIndex(action); - const int32_t pointerId = entry.pointerProperties[pointerIndex].id; + const PointerProperties& pointer = entry.pointerProperties[pointerIndex]; // Outside targets should be added upon first dispatched DOWN event. That means, this should // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down. const bool isStylus = isPointerFromStylus(entry, pointerIndex); @@ -2328,7 +2350,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( findTouchedWindowAtLocked(displayId, x, y, isStylus); if (isDown) { - targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId); + targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id); } // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { @@ -2386,7 +2408,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (isHoverAction) { // The "windowHandle" is the target of this hovering pointer. - tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId); + tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer); } // Set target flags. @@ -2409,12 +2431,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Update the temporary touch state. if (!isHoverAction) { - std::bitset<MAX_POINTER_ID + 1> pointerIds; - pointerIds.set(pointerId); const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN; tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::DispatchMode::AS_IS, - targetFlags, entry.deviceId, pointerIds, + targetFlags, entry.deviceId, {pointer}, isDownOrPointerDown ? std::make_optional(entry.eventTime) : std::nullopt); @@ -2437,7 +2457,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } tempTouchState.addOrUpdateWindow(wallpaper, InputTarget::DispatchMode::AS_IS, - wallpaperFlags, entry.deviceId, pointerIds, + wallpaperFlags, entry.deviceId, {pointer}, entry.eventTime); } } @@ -2448,12 +2468,12 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // make it pilfering. This will prevent other non-spy windows from getting this pointer, // which is a specific behaviour that we want. for (TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) && + if (touchedWindow.hasTouchingPointer(entry.deviceId, pointer.id) && touchedWindow.hasPilferingPointers(entry.deviceId)) { // This window is already pilfering some pointers, and this new pointer is also // going to it. Therefore, take over this pointer and don't give it to anyone // else. - touchedWindow.addPilferingPointer(entry.deviceId, pointerId); + touchedWindow.addPilferingPointer(entry.deviceId, pointer.id); } } @@ -2522,8 +2542,8 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Make a slippery exit from the old window. std::bitset<MAX_POINTER_ID + 1> pointerIds; - const int32_t pointerId = entry.pointerProperties[0].id; - pointerIds.set(pointerId); + const PointerProperties& pointer = entry.pointerProperties[0]; + pointerIds.set(pointer.id); const TouchedWindow& touchedWindow = tempTouchState.getTouchedWindow(oldTouchedWindowHandle); @@ -2553,13 +2573,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, InputTarget::DispatchMode::SLIPPERY_ENTER, - targetFlags, entry.deviceId, pointerIds, + targetFlags, entry.deviceId, {pointer}, entry.eventTime); // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, - tempTouchState, entry.deviceId, pointerId, targets); - tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId, + tempTouchState, entry.deviceId, pointer, targets); + tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id, oldTouchedWindowHandle); } } @@ -2568,14 +2588,12 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { // If no split, we suppose all touched windows should receive pointer down. const int32_t pointerIndex = MotionEvent::getActionIndex(action); - for (size_t i = 0; i < tempTouchState.windows.size(); i++) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; + std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]}; + for (TouchedWindow& touchedWindow : tempTouchState.windows) { // Ignore drag window for it should just track one pointer. if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { continue; } - std::bitset<MAX_POINTER_ID + 1> touchingPointers; - touchingPointers.set(entry.pointerProperties[pointerIndex].id); touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers); } } @@ -2646,13 +2664,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Output targets from the touch state. for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - std::bitset<MAX_POINTER_ID + 1> touchingPointers = + std::vector<PointerProperties> touchingPointers = touchedWindow.getTouchingPointers(entry.deviceId); - if (touchingPointers.none()) { + if (touchingPointers.empty()) { continue; } addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode, - touchedWindow.targetFlags, touchingPointers, + touchedWindow.targetFlags, getPointerIds(touchingPointers), touchedWindow.getDownTimeInTarget(entry.deviceId), targets); } @@ -3371,27 +3389,40 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio dispatchEntry->resolvedFlags = resolvedFlags; if (resolvedAction != motionEntry.action) { + std::optional<std::vector<PointerProperties>> usingProperties; + std::optional<std::vector<PointerCoords>> usingCoords; + if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT || + resolvedAction == AMOTION_EVENT_ACTION_CANCEL) { + // This is a HOVER_EXIT or an ACTION_CANCEL event that was synthesized by + // the dispatcher, and therefore the coordinates of this event are currently + // incorrect. These events should use the coordinates of the last dispatched + // ACTION_MOVE or HOVER_MOVE. We need to query InputState to get this data. + const bool hovering = resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT; + std::optional<std::pair<std::vector<PointerProperties>, + std::vector<PointerCoords>>> + pointerInfo = + connection->inputState.getPointersOfLastEvent(motionEntry, + hovering); + if (pointerInfo) { + usingProperties = pointerInfo->first; + usingCoords = pointerInfo->second; + } + } // Generate a new MotionEntry with a new eventId using the resolved action and // flags. - resolvedMotion = - std::make_shared<MotionEntry>(mIdGenerator.nextId(), - motionEntry.injectionState, - motionEntry.eventTime, - motionEntry.deviceId, motionEntry.source, - motionEntry.displayId, - motionEntry.policyFlags, resolvedAction, - motionEntry.actionButton, resolvedFlags, - motionEntry.metaState, - motionEntry.buttonState, - motionEntry.classification, - motionEntry.edgeFlags, - motionEntry.xPrecision, - motionEntry.yPrecision, - motionEntry.xCursorPosition, - motionEntry.yCursorPosition, - motionEntry.downTime, - motionEntry.pointerProperties, - motionEntry.pointerCoords); + resolvedMotion = std::make_shared< + MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState, + motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, + motionEntry.policyFlags, resolvedAction, + motionEntry.actionButton, resolvedFlags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, + usingProperties.value_or(motionEntry.pointerProperties), + usingCoords.value_or(motionEntry.pointerCoords)); if (ATRACE_ENABLED()) { std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32 ") to MotionEvent(id=0x%" PRIx32 ").", @@ -4418,7 +4449,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { policyFlags |= POLICY_FLAG_TRUSTED; android::base::Timer t; - mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags); + mPolicy.interceptMotionBeforeQueueing(args.displayId, args.source, args.action, args.eventTime, + policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -4679,7 +4711,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev if (!(policyFlags & POLICY_FLAG_FILTERED)) { nsecs_t eventTime = motionEvent.getEventTime(); android::base::Timer t; - mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); + mPolicy.interceptMotionBeforeQueueing(displayId, motionEvent.getSource(), + motionEvent.getAction(), eventTime, + /*byref*/ policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -5047,6 +5081,13 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w return false; } + // Ignore touches if stylus is down anywhere on screen + if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) && + isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) { + LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active"; + return false; + } + return true; } @@ -5481,7 +5522,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; - std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId); + std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId); sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); @@ -5493,17 +5534,17 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< newTargetFlags |= InputTarget::Flags::FOREGROUND; } state->addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags, - deviceId, pointerIds, downTimeInTarget); + deviceId, pointers, downTimeInTarget); // Store the dragging window. if (isDragDrop) { - if (pointerIds.count() != 1) { + if (pointers.size() != 1) { ALOGW("The drag and drop cannot be started when there is no pointer or more than 1" " pointer on the window."); return false; } // Track the pointer id for drag window and generate the drag state. - const size_t id = firstMarkedBit(pointerIds); + const size_t id = pointers.begin()->id; mDragState = std::make_unique<DragState>(toWindowHandle, id); } @@ -5520,7 +5561,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Check if the wallpaper window should deliver the corresponding event. transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle, - *state, deviceId, pointerIds); + *state, deviceId, pointers); } } // release lock @@ -5997,8 +6038,10 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { "input channel stole pointer stream"); options.deviceId = deviceId; options.displayId = displayId; - std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId); + std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId); + std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers); options.pointerIds = pointerIds; + std::string canceledWindows; for (const TouchedWindow& w : state.windows) { const std::shared_ptr<InputChannel> channel = @@ -6794,10 +6837,10 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle, const sp<WindowInfoHandle>& newWindowHandle, - TouchState& state, int32_t deviceId, int32_t pointerId, + TouchState& state, int32_t deviceId, + const PointerProperties& pointerProperties, std::vector<InputTarget>& targets) const { - std::bitset<MAX_POINTER_ID + 1> pointerIds; - pointerIds.set(pointerId); + std::vector<PointerProperties> pointers{pointerProperties}; const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) && @@ -6814,16 +6857,16 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl if (oldWallpaper != nullptr) { const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper); addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT, - oldTouchedWindow.targetFlags, pointerIds, + oldTouchedWindow.targetFlags, getPointerIds(pointers), oldTouchedWindow.getDownTimeInTarget(deviceId), targets); - state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper); + state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper); } if (newWallpaper != nullptr) { state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointerIds); + deviceId, pointers); } } @@ -6832,7 +6875,7 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT const sp<WindowInfoHandle> fromWindowHandle, const sp<WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, - std::bitset<MAX_POINTER_ID + 1> pointerIds) { + const std::vector<PointerProperties>& pointers) { const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) && fromWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -6861,7 +6904,7 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, - deviceId, pointerIds, downTimeInTarget); + deviceId, pointers, downTimeInTarget); std::shared_ptr<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 3f99b2d40e..010dbb28e4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -690,14 +690,15 @@ private: void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<android::gui::WindowInfoHandle>& oldWindowHandle, const sp<android::gui::WindowInfoHandle>& newWindowHandle, - TouchState& state, int32_t deviceId, int32_t pointerId, + TouchState& state, int32_t deviceId, + const PointerProperties& pointerProperties, std::vector<InputTarget>& targets) const REQUIRES(mLock); void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags, ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<android::gui::WindowInfoHandle> fromWindowHandle, const sp<android::gui::WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, - std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock); + const std::vector<PointerProperties>& pointers) REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index a4ac4fbe6c..1fec9b7599 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -195,6 +195,16 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t flags) { } } +std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>> +InputState::getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const { + ssize_t index = findMotionMemento(entry, hovering); + if (index == -1) { + return std::nullopt; + } + return std::make_pair(mMotionMementos[index].pointerProperties, + mMotionMementos[index].pointerCoords); +} + ssize_t InputState::findKeyMemento(const KeyEntry& entry) const { for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos[i]; @@ -311,16 +321,6 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons return true; } - // Use the previous stream cancellation logic to generate all HOVER_EXIT events. - // If this hover event was generated as a result of the pointer leaving the window, - // the HOVER_EXIT event should have the same coordinates as the previous - // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same - // coordinates as the previous event by cancelling the stream here. With this approach, the - // HOVER_EXIT event is generated from the previous event. - if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) { - return true; - } - // If the stream changes its source, just cancel the current gesture to be safe. It's // possible that the app isn't handling source changes properly if (motionEntry.source != lastMemento.source) { diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index b0e4209882..d49469ddc1 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -48,6 +48,15 @@ public: // and should be skipped. bool trackMotion(const MotionEntry& entry, int32_t flags); + /** + * Return the PointerProperties and the PointerCoords for the last event, if found. Return + * std::nullopt if not found. We should not return std::vector<PointerCoords> in isolation, + * because the pointers can technically be stored in the vector in any order, so the + * PointerProperties are needed to specify the order in which the pointer coords are stored. + */ + std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>> + getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const; + // Create cancel events for the previous stream if the current motionEntry requires it. std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry); diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index e8d8c18e4e..f8aa62500e 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -73,9 +73,9 @@ void TouchState::clearWindowsWithoutPointers() { void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds, + const std::vector<PointerProperties>& touchingPointers, std::optional<nsecs_t> firstDownTimeInTarget) { - if (touchingPointerIds.none()) { + if (touchingPointers.empty()) { LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName(); return; } @@ -91,7 +91,7 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have // downTime set initially. Need to update existing window when a pointer is down for the // window. - touchedWindow.addTouchingPointers(deviceId, touchingPointerIds); + touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); } @@ -102,7 +102,7 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, touchedWindow.windowHandle = windowHandle; touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags = targetFlags; - touchedWindow.addTouchingPointers(deviceId, touchingPointerIds); + touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); } @@ -110,17 +110,17 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, } void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle, - DeviceId deviceId, int32_t hoveringPointerId) { + DeviceId deviceId, const PointerProperties& pointer) { for (TouchedWindow& touchedWindow : windows) { if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.addHoveringPointer(deviceId, hoveringPointerId); + touchedWindow.addHoveringPointer(deviceId, pointer); return; } } TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; - touchedWindow.addHoveringPointer(deviceId, hoveringPointerId); + touchedWindow.addHoveringPointer(deviceId, pointer); windows.push_back(touchedWindow); } @@ -234,6 +234,11 @@ bool TouchState::hasHoveringPointers(DeviceId deviceId) const { }); } +bool TouchState::hasActiveStylus() const { + return std::any_of(windows.begin(), windows.end(), + [](const TouchedWindow& window) { return window.hasActiveStylus(); }); +} + std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId, int32_t pointerId) const { std::set<sp<WindowInfoHandle>> out; diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index e0a84e8f45..3d534bc71d 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -46,11 +46,11 @@ struct TouchState { void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds, + const std::vector<PointerProperties>& touchingPointers, std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, - DeviceId deviceId, int32_t hoveringPointerId); - void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId); + DeviceId deviceId, const PointerProperties& pointer); + void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); void clearHoveringPointers(DeviceId deviceId); void removeAllPointersForDevice(DeviceId deviceId); @@ -73,6 +73,8 @@ struct TouchState { bool isDown(DeviceId deviceId) const; bool hasHoveringPointers(DeviceId deviceId) const; + bool hasActiveStylus() const; + std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer( DeviceId deviceId, int32_t pointerId) const; std::string dump() const; diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index cd0500c872..037d7c8e99 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -26,9 +26,20 @@ namespace android { namespace inputdispatcher { +namespace { + +bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointerId) { + return std::find_if(pointers.begin(), pointers.end(), + [&pointerId](const PointerProperties& properties) { + return properties.id == pointerId; + }) != pointers.end(); +} + +} // namespace + bool TouchedWindow::hasHoveringPointers() const { for (const auto& [_, state] : mDeviceStates) { - if (state.hoveringPointerIds.any()) { + if (!state.hoveringPointers.empty()) { return true; } } @@ -42,7 +53,7 @@ bool TouchedWindow::hasHoveringPointers(DeviceId deviceId) const { } const DeviceState& state = stateIt->second; - return state.hoveringPointerIds.any(); + return !state.hoveringPointers.empty(); } void TouchedWindow::clearHoveringPointers(DeviceId deviceId) { @@ -51,7 +62,7 @@ void TouchedWindow::clearHoveringPointers(DeviceId deviceId) { return; } DeviceState& state = stateIt->second; - state.hoveringPointerIds.reset(); + state.hoveringPointers.clear(); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); } @@ -63,22 +74,40 @@ bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) con return false; } const DeviceState& state = stateIt->second; - - return state.hoveringPointerIds.test(pointerId); + return hasPointerId(state.hoveringPointers, pointerId); } -void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) { - mDeviceStates[deviceId].hoveringPointerIds.set(pointerId); +void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) { + std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers; + const size_t initialSize = hoveringPointers.size(); + std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) { + return properties.id == pointer.id; + }); + if (hoveringPointers.size() != initialSize) { + LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this; + } + hoveringPointers.push_back(pointer); } void TouchedWindow::addTouchingPointers(DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> pointers) { - mDeviceStates[deviceId].touchingPointerIds |= pointers; + const std::vector<PointerProperties>& pointers) { + std::vector<PointerProperties>& touchingPointers = mDeviceStates[deviceId].touchingPointers; + const size_t initialSize = touchingPointers.size(); + for (const PointerProperties& pointer : pointers) { + std::erase_if(touchingPointers, [&pointer](const PointerProperties& properties) { + return properties.id == pointer.id; + }); + } + if (touchingPointers.size() != initialSize) { + LOG(ERROR) << __func__ << ": " << dumpVector(pointers, streamableToString) << ", device " + << deviceId << " already in " << *this; + } + touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end()); } bool TouchedWindow::hasTouchingPointers() const { for (const auto& [_, state] : mDeviceStates) { - if (state.touchingPointerIds.any()) { + if (!state.touchingPointers.empty()) { return true; } } @@ -86,21 +115,25 @@ bool TouchedWindow::hasTouchingPointers() const { } bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const { - return getTouchingPointers(deviceId).any(); + return !getTouchingPointers(deviceId).empty(); } bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const { - return getTouchingPointers(deviceId).test(pointerId); + const auto stateIt = mDeviceStates.find(deviceId); + if (stateIt == mDeviceStates.end()) { + return false; + } + const DeviceState& state = stateIt->second; + return hasPointerId(state.touchingPointers, pointerId); } -std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const { +std::vector<PointerProperties> TouchedWindow::getTouchingPointers(DeviceId deviceId) const { const auto stateIt = mDeviceStates.find(deviceId); if (stateIt == mDeviceStates.end()) { return {}; } const DeviceState& state = stateIt->second; - - return state.touchingPointerIds; + return state.touchingPointers; } void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) { @@ -118,7 +151,10 @@ void TouchedWindow::removeTouchingPointers(DeviceId deviceId, } DeviceState& state = stateIt->second; - state.touchingPointerIds &= ~pointers; + std::erase_if(state.touchingPointers, [&pointers](const PointerProperties& properties) { + return pointers.test(properties.id); + }); + state.pilferingPointerIds &= ~pointers; if (!state.hasPointers()) { @@ -126,10 +162,26 @@ void TouchedWindow::removeTouchingPointers(DeviceId deviceId, } } +bool TouchedWindow::hasActiveStylus() const { + for (const auto& [_, state] : mDeviceStates) { + for (const PointerProperties& properties : state.touchingPointers) { + if (properties.toolType == ToolType::STYLUS) { + return true; + } + } + for (const PointerProperties& properties : state.hoveringPointers) { + if (properties.toolType == ToolType::STYLUS) { + return true; + } + } + } + return false; +} + std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const { std::set<DeviceId> deviceIds; for (const auto& [deviceId, deviceState] : mDeviceStates) { - if (deviceState.touchingPointerIds.any()) { + if (!deviceState.touchingPointers.empty()) { deviceIds.insert(deviceId); } } @@ -198,7 +250,7 @@ void TouchedWindow::removeAllTouchingPointersForDevice(DeviceId deviceId) { } DeviceState& state = stateIt->second; - state.touchingPointerIds.reset(); + state.touchingPointers.clear(); state.pilferingPointerIds.reset(); state.downTimeInTarget.reset(); @@ -214,7 +266,9 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } DeviceState& state = stateIt->second; - state.hoveringPointerIds.set(pointerId, false); + std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) { + return properties.id == pointerId; + }); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); @@ -228,7 +282,7 @@ void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) { } DeviceState& state = stateIt->second; - state.hoveringPointerIds.reset(); + state.hoveringPointers.clear(); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); @@ -236,11 +290,11 @@ void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) { } std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) { - return StringPrintf("[touchingPointerIds=%s, " - "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]", - bitsetToString(state.touchingPointerIds).c_str(), + return StringPrintf("[touchingPointers=%s, " + "downTimeInTarget=%s, hoveringPointers=%s, pilferingPointerIds=%s]", + dumpVector(state.touchingPointers, streamableToString).c_str(), toString(state.downTimeInTarget).c_str(), - bitsetToString(state.hoveringPointerIds).c_str(), + dumpVector(state.hoveringPointers, streamableToString).c_str(), bitsetToString(state.pilferingPointerIds).c_str()); } diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index c604353593..0d1531f8ff 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -38,17 +38,18 @@ struct TouchedWindow { bool hasHoveringPointers() const; bool hasHoveringPointers(DeviceId deviceId) const; bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const; - void addHoveringPointer(DeviceId deviceId, int32_t pointerId); + void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); // Touching bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const; bool hasTouchingPointers() const; bool hasTouchingPointers(DeviceId deviceId) const; - std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const; - void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); + std::vector<PointerProperties> getTouchingPointers(DeviceId deviceId) const; + void addTouchingPointers(DeviceId deviceId, const std::vector<PointerProperties>& pointers); void removeTouchingPointer(DeviceId deviceId, int32_t pointerId); void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); + bool hasActiveStylus() const; std::set<DeviceId> getTouchingDeviceIds() const; // Pilfering pointers @@ -69,16 +70,16 @@ struct TouchedWindow { private: struct DeviceState { - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds; + std::vector<PointerProperties> touchingPointers; // The pointer ids of the pointers that this window is currently pilfering, by device std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds; // Time at which the first action down occurred on this window, for each device // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE // scenario. std::optional<nsecs_t> downTimeInTarget; - std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds; + std::vector<PointerProperties> hoveringPointers; - bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); }; + bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); }; }; std::map<DeviceId, DeviceState> mDeviceStates; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 1c23720ecb..9e6209b3d9 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -99,8 +99,8 @@ public: * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event * should be dispatched to applications. */ - virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when, - uint32_t& policyFlags) = 0; + virtual void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action, + nsecs_t when, uint32_t& policyFlags) = 0; /* Allows the policy a chance to intercept a key before dispatching. */ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 5a74a42446..2dd05f5c66 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -35,10 +35,12 @@ MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext, MultiTouchInputMapper::~MultiTouchInputMapper() {} std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) { - // TODO(b/291626046): Sync the MT state with the kernel using EVIOCGMTSLOTS. - mMultiTouchMotionAccumulator.reset(getDeviceContext()); - mPointerIdBits.clear(); - + // The evdev multi-touch protocol does not allow userspace applications to query the initial or + // current state of the pointers at any time. This means if we clear our accumulated state when + // resetting the input mapper, there's no way to rebuild the full initial state of the pointers. + // We can only wait for updates to all the pointers and axes. Rather than clearing the state and + // rebuilding the state from scratch, we work around this kernel API limitation by never + // fully clearing any state specific to the multi-touch protocol. return TouchInputMapper::reset(when); } diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 34ca0b3767..255f02d278 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -21,9 +21,11 @@ #include <iterator> #include <limits> #include <map> +#include <mutex> #include <optional> #include <android-base/stringprintf.h> +#include <android-base/thread_annotations.h> #include <android/input.h> #include <com_android_input_flags.h> #include <ftl/enum.h> @@ -156,13 +158,20 @@ public: return sAccumulator; } - void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; } + void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { + std::scoped_lock lock(mLock); + mCounters[id].fingers++; + } - void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; } + void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { + std::scoped_lock lock(mLock); + mCounters[id].palms++; + } // Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and // records it if so. void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) { + std::scoped_lock lock(mLock); switch (gesture.type) { case kGestureTypeFling: if (gesture.details.fling.fling_state == GESTURES_FLING_START) { @@ -200,15 +209,20 @@ private: void* cookie) { LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE); MetricsAccumulator& accumulator = MetricsAccumulator::getInstance(); - accumulator.produceAtoms(outEventList); - accumulator.resetCounters(); + accumulator.produceAtomsAndReset(*outEventList); return AStatsManager_PULL_SUCCESS; } - void produceAtoms(AStatsEventList* outEventList) const { + void produceAtomsAndReset(AStatsEventList& outEventList) { + std::scoped_lock lock(mLock); + produceAtomsLocked(outEventList); + resetCountersLocked(); + } + + void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) { for (auto& [id, counters] : mCounters) { auto [busId, vendorId, productId, versionId] = id; - addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId, + addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId, versionId, linuxBusToInputDeviceBusEnum(busId, /*isUsi=*/false), counters.fingers, counters.palms, counters.twoFingerSwipeGestures, counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures, @@ -216,7 +230,7 @@ private: } } - void resetCounters() { mCounters.clear(); } + void resetCountersLocked() REQUIRES(mLock) { mCounters.clear(); } // Stores the counters for a specific touchpad model. Fields have the same meanings as those of // the TouchpadUsage atom; see that definition for detailed documentation. @@ -232,7 +246,10 @@ private: // Metrics are aggregated by device model and version, so if two devices of the same model and // version are connected at once, they will have the same counters. - std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters; + std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters GUARDED_BY(mLock); + + // Metrics are pulled by a binder thread, so we need to guard them with a mutex. + mutable std::mutex mLock; }; } // namespace @@ -449,6 +466,9 @@ std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) { if (mPointerCaptured) { return mCapturedEventConverter.process(*rawEvent); } + if (mMotionAccumulator.getActiveSlotsCount() == 0) { + mGestureStartTime = rawEvent->when; + } std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent); if (state) { updatePalmDetectionMetrics(); @@ -514,7 +534,7 @@ std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t if (mDisplayId) { MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance(); for (Gesture& gesture : mGesturesToProcess) { - out += mGestureConverter.handleGesture(when, readTime, gesture); + out += mGestureConverter.handleGesture(when, readTime, mGestureStartTime, gesture); metricsAccumulator.processGesture(mMetricsId, gesture); } } diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index ece0eca0e7..897edca4e1 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -113,6 +113,8 @@ private: // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. // std::nullopt), all events will be ignored. std::optional<int32_t> mDisplayId; + + nsecs_t mGestureStartTime{0}; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index d06514acab..b0fc9035f2 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -30,29 +30,23 @@ void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceCont size_t slotCount, bool usingSlotsProtocol) { mUsingSlotsProtocol = usingSlotsProtocol; mSlots = std::vector<Slot>(slotCount); - reset(deviceContext); -} - -void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) { - resetSlots(); - - if (!mUsingSlotsProtocol) { - return; - } - // Query the driver for the current slot index and use it as the initial slot before we - // start reading events from the device. It is possible that the current slot index will - // not be the same as it was when the first event was written into the evdev buffer, which - // means the input mapper could start out of sync with the initial state of the events in - // the evdev buffer. In the extremely unlikely case that this happens, the data from two - // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the - // touch point to "jump", but at least there will be no stuck touches. - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; - } else { - ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); + mCurrentSlot = -1; + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot before we + // start reading events from the device. It is possible that the current slot index will + // not be the same as it was when the first event was written into the evdev buffer, which + // means the input mapper could start out of sync with the initial state of the events in + // the evdev buffer. In the extremely unlikely case that this happens, the data from two + // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the + // touch point to "jump", but at least there will be no stuck touches. + int32_t initialSlot; + if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); + status == OK) { + mCurrentSlot = initialSlot; + } else { + ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); + } } } diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index 5b55e3d599..0e3e2bb365 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -83,7 +83,6 @@ public: LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); return mSlots[index]; } - void reset(const InputDeviceContext& deviceContext); private: int32_t mCurrentSlot; diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 955210479f..01e983a371 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -35,8 +35,14 @@ namespace android { namespace { +// This will disable the tap to click while the user is typing on a physical keyboard const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection(); +// In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in +// re-enabling the tap to click. +const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 = + input_flags::enable_v2_touchpad_typing_palm_rejection(); + uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { switch (gesturesButton) { case GESTURES_BUTTON_LEFT: @@ -75,6 +81,7 @@ std::string GestureConverter::dump() const { out << StringPrintf("Button state: 0x%08x\n", mButtonState); out << "Down time: " << mDownTime << "\n"; out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n"; + out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n"; return out.str(); } @@ -109,8 +116,6 @@ std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) { void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0); - // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture - // is enabled. if (!mBoundsInLogicalDisplay.isEmpty()) { info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left, mBoundsInLogicalDisplay.right, 0, 0, 0); @@ -131,6 +136,7 @@ void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { } std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture) { if (!mDisplayId) { // Ignore gestures when there is no target display configured. @@ -139,13 +145,13 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read switch (gesture.type) { case kGestureTypeMove: - return {handleMove(when, readTime, gesture)}; + return handleMove(when, readTime, gestureStartTime, gesture); case kGestureTypeButtonsChange: return handleButtonsChange(when, readTime, gesture); case kGestureTypeScroll: return handleScroll(when, readTime, gesture); case kGestureTypeFling: - return handleFling(when, readTime, gesture); + return handleFling(when, readTime, gestureStartTime, gesture); case kGestureTypeSwipe: return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx, gesture.details.swipe.dy); @@ -162,18 +168,41 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read } } -NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, - const Gesture& gesture) { +std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, + const Gesture& gesture) { float deltaX = gesture.details.move.dx; float deltaY = gesture.details.move.dy; - if (ENABLE_TOUCHPAD_PALM_REJECTION && (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) { - enableTapToClick(); + if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { + bool wasHoverCancelled = mIsHoverCancelled; + // Gesture will be cancelled if it started before the user started typing and + // there is a active IME connection. + mIsHoverCancelled = gestureStartTime <= mReaderContext.getLastKeyDownTimestamp() && + mReaderContext.getPolicy()->isInputMethodConnectionActive(); + + if (!wasHoverCancelled && mIsHoverCancelled) { + // This is the first event of the cancelled gesture, we won't return because we need to + // generate a HOVER_EXIT event + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + } else if (mIsHoverCancelled) { + return {}; + } } + rotateDelta(mOrientation, &deltaX, &deltaY); - mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); - mPointerController->move(deltaX, deltaY); - mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + // Update the cursor, and enable tap to click if the gesture is not cancelled + if (!mIsHoverCancelled) { + // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click + // for this case as subsequent handleButtonsChange may choose to ignore this tap. + if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) && + (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) { + enableTapToClick(when); + } + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); + mPointerController->move(deltaX, deltaY); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); @@ -187,10 +216,12 @@ NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const bool down = isPointerDown(mButtonState); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; - return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, - /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition); + const int32_t action = mIsHoverCancelled + ? AMOTION_EVENT_ACTION_HOVER_EXIT + : (down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE); + return {makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, + /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, + yCursorPosition)}; } std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime, @@ -210,8 +241,15 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); - if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) { - enableTapToClick(); + // V2 palm rejection should override V1 + if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { + enableTapToClick(when); + if (gesture.details.buttons.is_tap && when <= mWhenToEnableTapToClick) { + // return early to prevent this tap + return out; + } + } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) { + enableTapToClick(when); if (gesture.details.buttons.is_tap) { // return early to prevent this tap return out; @@ -348,6 +386,7 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT } std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture) { switch (gesture.details.fling.fling_state) { case GESTURES_FLING_START: @@ -366,13 +405,10 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi // magnitude, which will also result in the pointer icon being updated. // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been // initiated with a touchpad. - if (!mReaderContext.isPreventingTouchpadTaps()) { - enableTapToClick(); - } - return {handleMove(when, readTime, - Gesture(kGestureMove, gesture.start_time, gesture.end_time, - /*dx=*/0.f, - /*dy=*/0.f))}; + return handleMove(when, readTime, gestureStartTime, + Gesture(kGestureMove, gesture.start_time, gesture.end_time, + /*dx=*/0.f, + /*dy=*/0.f)); } break; default: @@ -598,8 +634,11 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime /* videoFrames= */ {}}; } -void GestureConverter::enableTapToClick() { - mReaderContext.setPreventingTouchpadTaps(false); +void GestureConverter::enableTapToClick(nsecs_t when) { + if (mReaderContext.isPreventingTouchpadTaps()) { + mWhenToEnableTapToClick = when + TAP_ENABLE_DELAY_NANOS.count(); + mReaderContext.setPreventingTouchpadTaps(false); + } } } // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 732a4b2ffb..88e7b99877 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -34,6 +34,13 @@ namespace android { +using std::chrono_literals::operator""ms; +/** + * This duration is decided based on internal team testing, it may be updated after testing with + * larger groups + */ +constexpr std::chrono::nanoseconds TAP_ENABLE_DELAY_NANOS = 400ms; + // Converts Gesture structs from the gestures library into NotifyArgs and the appropriate // PointerController calls. class GestureConverter { @@ -53,17 +60,20 @@ public: void populateMotionRanges(InputDeviceInfo& info) const; [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture); private: - [[nodiscard]] NotifyMotionArgs handleMove(nsecs_t when, nsecs_t readTime, - const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleMove(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, + const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture); [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime); @@ -82,7 +92,9 @@ private: const PointerCoords* pointerCoords, float xCursorPosition, float yCursorPosition); - void enableTapToClick(); + void enableTapToClick(nsecs_t when); + bool mIsHoverCancelled{false}; + nsecs_t mWhenToEnableTapToClick{0}; const int32_t mDeviceId; InputReaderContext& mReaderContext; diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 340ff8e296..e94a71fbf8 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -27,6 +27,7 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use crate::bounce_keys_filter::BounceKeysFilter; +use crate::sticky_keys_filter::StickyKeysFilter; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; @@ -91,6 +92,14 @@ impl IInputFilter for InputFilter { let mut state = self.state.lock().unwrap(); let mut first_filter: Box<dyn Filter + Send + Sync> = Box::new(BaseFilter::new(self.callbacks.clone())); + if config.stickyKeysEnabled { + first_filter = Box::new(StickyKeysFilter::new( + first_filter, + ModifierStateListener::new(self.callbacks.clone()), + )); + state.enabled = true; + info!("Sticky keys filter is installed"); + } if config.bounceKeysThresholdNs > 0 { first_filter = Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); @@ -125,34 +134,43 @@ impl Filter for BaseFilter { } } +pub struct ModifierStateListener { + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, +} + +impl ModifierStateListener { + /// Create a new InputFilter instance. + pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener { + Self { callbacks } + } + + pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) { + let _ = self + .callbacks + .read() + .unwrap() + .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32); + } +} + #[cfg(test)] mod tests { - use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter}; + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter, + }; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; - use binder::{Interface, Strong}; + use binder::Strong; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, IInputFilter::IInputFilter, - IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; use std::sync::{Arc, RwLock}; - struct FakeCallbacks {} - - impl Interface for FakeCallbacks {} - - impl IInputFilterCallbacks for FakeCallbacks { - fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> { - Result::Ok(()) - } - } - #[test] fn test_not_enabled_with_default_filter() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(!result.unwrap()); @@ -160,17 +178,21 @@ mod tests { #[test] fn test_notify_key_with_no_filters() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone()))); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); + assert_eq!(test_callbacks.last_event().unwrap(), event); } #[test] fn test_notify_key_with_filter() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); assert_eq!(test_filter.last_event().unwrap(), event); @@ -179,7 +201,11 @@ mod tests { #[test] fn test_notify_devices_changed() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); assert!(input_filter .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }]) .is_ok()); @@ -188,21 +214,30 @@ mod tests { #[test] fn test_notify_configuration_changed_enabled_bounce_keys() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); - let result = input_filter - .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 }); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + bounceKeysThresholdNs: 100, + stickyKeysEnabled: false, + }); assert!(result.is_ok()); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(result.unwrap()); } - fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks))) + #[test] + fn test_notify_configuration_changed_enabled_sticky_keys() { + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + bounceKeysThresholdNs: 0, + stickyKeysEnabled: true, + }); + assert!(result.is_ok()); + let result = input_filter.isEnabled(); + assert!(result.is_ok()); + assert!(result.unwrap()); } fn create_key_event() -> KeyEvent { @@ -272,3 +307,69 @@ pub mod test_filter { } } } + +#[cfg(test)] +pub mod test_callbacks { + use binder::Interface; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, + }; + use std::sync::{Arc, RwLock, RwLockWriteGuard}; + + #[derive(Default)] + struct TestCallbacksInner { + last_modifier_state: u32, + last_locked_modifier_state: u32, + last_event: Option<KeyEvent>, + } + + #[derive(Default, Clone)] + pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>); + + impl Interface for TestCallbacks {} + + impl TestCallbacks { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> { + self.0.write().unwrap() + } + + pub fn last_event(&self) -> Option<KeyEvent> { + self.0.read().unwrap().last_event + } + + pub fn clear(&mut self) { + self.inner().last_event = None; + self.inner().last_modifier_state = 0; + self.inner().last_locked_modifier_state = 0; + } + + pub fn get_last_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_modifier_state + } + + pub fn get_last_locked_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_locked_modifier_state + } + } + + impl IInputFilterCallbacks for TestCallbacks { + fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> { + self.inner().last_event = Some(*event); + Result::Ok(()) + } + + fn onModifierStateChanged( + &self, + modifier_state: i32, + locked_modifier_state: i32, + ) -> std::result::Result<(), binder::Status> { + self.inner().last_modifier_state = modifier_state as u32; + self.inner().last_locked_modifier_state = locked_modifier_state as u32; + Result::Ok(()) + } + } +} diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index 68cd4800fe..fa16898835 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -21,6 +21,7 @@ mod bounce_keys_filter; mod input_filter; +mod sticky_keys_filter; use crate::input_filter::InputFilter; use binder::{ diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs new file mode 100644 index 0000000000..da581b82bf --- /dev/null +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -0,0 +1,515 @@ +/* + * Copyright 2023 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. + */ + +//! Sticky keys input filter implementation. +//! Sticky keys is an accessibility feature that assists users who have physical disabilities or +//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing +//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift, +//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed. +use crate::input_filter::{Filter, ModifierStateListener}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, +}; +use std::collections::HashSet; + +// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h +const KEYCODE_ALT_LEFT: i32 = 57; +const KEYCODE_ALT_RIGHT: i32 = 58; +const KEYCODE_SHIFT_LEFT: i32 = 59; +const KEYCODE_SHIFT_RIGHT: i32 = 60; +const KEYCODE_SYM: i32 = 63; +const KEYCODE_CTRL_LEFT: i32 = 113; +const KEYCODE_CTRL_RIGHT: i32 = 114; +const KEYCODE_CAPS_LOCK: i32 = 115; +const KEYCODE_SCROLL_LOCK: i32 = 116; +const KEYCODE_META_LEFT: i32 = 117; +const KEYCODE_META_RIGHT: i32 = 118; +const KEYCODE_FUNCTION: i32 = 119; +const KEYCODE_NUM_LOCK: i32 = 143; + +// Modifier states: values are from /frameworks/native/include/android/input.h +const META_ALT_ON: u32 = 0x02; +const META_ALT_LEFT_ON: u32 = 0x10; +const META_ALT_RIGHT_ON: u32 = 0x20; +const META_SHIFT_ON: u32 = 0x01; +const META_SHIFT_LEFT_ON: u32 = 0x40; +const META_SHIFT_RIGHT_ON: u32 = 0x80; +const META_CTRL_ON: u32 = 0x1000; +const META_CTRL_LEFT_ON: u32 = 0x2000; +const META_CTRL_RIGHT_ON: u32 = 0x4000; +const META_META_ON: u32 = 0x10000; +const META_META_LEFT_ON: u32 = 0x20000; +const META_META_RIGHT_ON: u32 = 0x40000; + +pub struct StickyKeysFilter { + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + /// Tracking devices that contributed to the modifier state. + contributing_devices: HashSet<i32>, + /// State describing the current enabled modifiers. This contain both locked and non-locked + /// modifier state bits. + modifier_state: u32, + /// State describing the current locked modifiers. These modifiers will not be cleared on a + /// non-modifier key press. They will be cleared only if the locked modifier key is pressed + /// again. + locked_modifier_state: u32, +} + +impl StickyKeysFilter { + /// Create a new StickyKeysFilter instance. + pub fn new( + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + ) -> StickyKeysFilter { + Self { + next, + listener, + contributing_devices: HashSet::new(), + modifier_state: 0, + locked_modifier_state: 0, + } + } +} + +impl Filter for StickyKeysFilter { + fn notify_key(&mut self, event: &KeyEvent) { + let up = event.action == KeyEventAction::UP; + let mut modifier_state = self.modifier_state; + let mut locked_modifier_state = self.locked_modifier_state; + if !is_ephemeral_modifier_key(event.keyCode) { + // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like + // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with + // the KeyEvent. + let old_modifier_state = event.metaState as u32; + let mut new_event = *event; + // Send the current modifier state with the key event before clearing non-locked + // modifier state + new_event.metaState = + (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32; + self.next.notify_key(&new_event); + if up && !is_modifier_key(event.keyCode) { + modifier_state = + clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state; + } + } else if up { + // Update contributing devices to track keyboards + self.contributing_devices.insert(event.deviceId); + // If ephemeral modifier key, capture the key and update the sticky modifier states + let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode); + let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode); + if locked_modifier_state & modifier_key_mask != 0 { + locked_modifier_state &= !symmetrical_modifier_key_mask; + modifier_state &= !symmetrical_modifier_key_mask; + } else if modifier_key_mask & modifier_state != 0 { + locked_modifier_state |= modifier_key_mask; + modifier_state = + (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask; + } else { + modifier_state |= modifier_key_mask; + } + } + if self.modifier_state != modifier_state + || self.locked_modifier_state != locked_modifier_state + { + self.modifier_state = modifier_state; + self.locked_modifier_state = locked_modifier_state; + self.listener.modifier_state_changed(modifier_state, locked_modifier_state); + } + } + + fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { + // Clear state if all contributing devices removed + self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); + if self.contributing_devices.is_empty() + && (self.modifier_state != 0 || self.locked_modifier_state != 0) + { + self.modifier_state = 0; + self.locked_modifier_state = 0; + self.listener.modifier_state_changed(0, 0); + } + self.next.notify_devices_changed(device_infos); + } +} + +fn is_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + | KEYCODE_SYM + | KEYCODE_FUNCTION + | KEYCODE_CAPS_LOCK + | KEYCODE_NUM_LOCK + | KEYCODE_SCROLL_LOCK + ) +} + +fn is_ephemeral_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + ) +} + +fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON, + KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON, + KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON, + KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON, + KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON, + KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON, + KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON, + _ => 0, + } +} + +/// Modifier mask including both left and right versions of a modifier key. +fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => { + META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON + } + KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => { + META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON + } + KEYCODE_META_LEFT | KEYCODE_META_RIGHT => { + META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON + } + _ => 0, + } +} + +fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 { + modifier_state + & !(META_ALT_LEFT_ON + | META_ALT_RIGHT_ON + | META_ALT_ON + | META_SHIFT_LEFT_ON + | META_SHIFT_RIGHT_ON + | META_SHIFT_ON + | META_CTRL_LEFT_ON + | META_CTRL_RIGHT_ON + | META_CTRL_ON + | META_META_LEFT_ON + | META_META_RIGHT_ON + | META_META_ON) +} + +#[cfg(test)] +mod tests { + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener, + }; + use crate::sticky_keys_filter::{ + StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK, + KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON, + META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON, + META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON, + }; + use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; + use binder::Strong; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, + KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, + }; + use std::sync::{Arc, RwLock}; + + static DEVICE_ID: i32 = 1; + static KEY_A: i32 = 29; + static BASE_KEY_DOWN: KeyEvent = KeyEvent { + id: 1, + deviceId: DEVICE_ID, + downTime: 0, + readTime: 0, + eventTime: 0, + source: Source::KEYBOARD, + displayId: 0, + policyFlags: 0, + action: KeyEventAction::DOWN, + flags: 0, + keyCode: 0, + scanCode: 0, + metaState: 0, + }; + + static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN }; + + #[test] + fn test_notify_key_consumes_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_ALT_LEFT, + KEYCODE_ALT_RIGHT, + KEYCODE_CTRL_LEFT, + KEYCODE_CTRL_RIGHT, + KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, + KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, + ]; + for key_code in key_codes.iter() { + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }); + assert!(test_filter.last_event().is_none()); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }); + assert!(test_filter.last_event().is_none()); + } + } + + #[test] + fn test_notify_key_passes_non_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_CAPS_LOCK, + KEYCODE_NUM_LOCK, + KEYCODE_SCROLL_LOCK, + KEYCODE_FUNCTION, + KEYCODE_SYM, + ]; + for key_code in key_codes.iter() { + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + } + + #[test] + fn test_notify_key_passes_non_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + + #[test] + fn test_modifier_state_updated_on_modifier_key_press() { + let mut test_filter = TestFilter::new(); + let mut test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let test_states = &[ + (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON), + (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON), + (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON), + (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON), + (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON), + (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON), + (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON), + (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON), + ]; + for test_state in test_states.iter() { + test_filter.clear(); + test_callbacks.clear(); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + // Re-send keys to lock it + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + // Re-send keys to clear + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + } + + #[test] + fn test_modifier_state_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + #[test] + fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP }); + + assert_eq!( + test_callbacks.get_last_modifier_state(), + META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON + ); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_key_events_have_sticky_modifier_state() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_modifier_state_not_cleared_until_all_devices_removed() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]); + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_devices_changed(&[]); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + fn setup_filter( + next: Box<dyn Filter + Send + Sync>, + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + ) -> StickyKeysFilter { + StickyKeysFilter::new(next, ModifierStateListener::new(callbacks)) + } +} diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index e9d93af67b..fb2db06aff 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -63,7 +63,7 @@ private: void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {} - void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {} nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { return 0; diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 7668011378..800f8649fd 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -41,6 +41,7 @@ public: void setDisplayViewport(const DisplayViewport& viewport) override; void updatePointerIcon(PointerIconStyle iconId) override; void setCustomPointerIcon(const SpriteIcon& icon) override; + void fade(Transition) override; void assertViewportSet(int32_t displayId); void assertViewportNotSet(); @@ -56,7 +57,6 @@ private: std::string dump() override { return ""; } std::optional<FloatRect> getBounds() const override; void move(float deltaX, float deltaY) override; - void fade(Transition) override; void unfade(Transition) override; void setPresentation(Presentation) override {} void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index d2b68dd93e..69772af6b3 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -41,10 +41,14 @@ namespace { const auto TOUCHPAD_PALM_REJECTION = ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection); +const auto TOUCHPAD_PALM_REJECTION_V2 = + ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection); } // namespace using testing::AllOf; +using testing::ElementsAre; +using testing::VariantWith; class GestureConverterTestBase : public testing::Test { protected: @@ -107,14 +111,14 @@ TEST_F(GestureConverterTest, Move) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); } @@ -126,14 +130,14 @@ TEST_F(GestureConverterTest, Move_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5)); } @@ -147,67 +151,69 @@ TEST_F(GestureConverterTest, ButtonsChange) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Then release the left button Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Finally release the right button Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, DragWithButton) { @@ -219,32 +225,33 @@ TEST_F(GestureConverterTest, DragWithButton) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Move Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); @@ -252,24 +259,27 @@ TEST_F(GestureConverterTest, DragWithButton) { Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll) { @@ -279,50 +289,52 @@ TEST_F(GestureConverterTest, Scroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_Rotated) { @@ -333,43 +345,47 @@ TEST_F(GestureConverterTest, Scroll_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 10, POINTER_Y), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { @@ -378,21 +394,22 @@ TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionClassification(MotionClassification::NONE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::NONE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) { @@ -401,20 +418,21 @@ TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON)); } @@ -426,17 +444,19 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5, /*dy=*/10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { @@ -446,16 +466,17 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5, /*dy=*/5); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0))); @@ -472,7 +493,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); // Three fake fingers should be created. We don't actually care where they are, so long as they @@ -523,7 +545,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -540,30 +562,36 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { @@ -574,7 +602,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); // Three fake fingers should be created. We don't actually care where they are, so long as they @@ -616,7 +645,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -631,23 +660,24 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { @@ -657,7 +687,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 10, /* dy= */ 0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(5u, args.size()); // Four fake fingers should be created. We don't actually care where they are, so long as they @@ -720,7 +751,7 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 5, /* dy= */ 0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -739,38 +770,46 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(4u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_Inwards) { @@ -780,51 +819,57 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(POINTER_X - 100, POINTER_Y), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(0.8f, EPSILON), - WithPointerCoords(0, POINTER_X - 80, POINTER_Y), - WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), + WithPointerCoords(0, POINTER_X - 80, POINTER_Y), + WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_Outwards) { @@ -834,51 +879,57 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(POINTER_X - 100, POINTER_Y), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.2f, EPSILON), - WithPointerCoords(0, POINTER_X - 120, POINTER_Y), - WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.2f, EPSILON), + WithPointerCoords(0, POINTER_X - 120, POINTER_Y), + WithPointerCoords(1, POINTER_X + 120, POINTER_Y), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) { @@ -888,21 +939,22 @@ TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) { @@ -912,21 +964,22 @@ TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like scroll. Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1, /*dy=*/0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON)); } @@ -939,28 +992,28 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringScroll) { @@ -969,18 +1022,18 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { @@ -990,31 +1043,35 @@ TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringPinch) { @@ -1024,22 +1081,24 @@ TEST_F(GestureConverterTest, ResetDuringPinch) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, FlingTapDown) { @@ -1049,7 +1108,8 @@ TEST_F(GestureConverterTest, FlingTapDown) { Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), @@ -1069,52 +1129,57 @@ TEST_F(GestureConverterTest, Tap) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); - - ASSERT_EQ(5u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Click) { @@ -1125,62 +1190,71 @@ TEST_F(GestureConverterTest, Click) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION), + REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); @@ -1188,22 +1262,22 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); - Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); // no events should be generated ASSERT_EQ(0u, args.size()); @@ -1212,18 +1286,21 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } -TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { - // Click should still produce button press/release events +TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), @@ -1232,49 +1309,143 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, - /* down= */ GESTURES_BUTTON_LEFT, - /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - ASSERT_EQ(2u, args.size()); + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); + + // taps before the threshold should still be ignored + currentTime += TAP_ENABLE_DELAY_NANOS.count(); + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // taps after the threshold should be recognised + currentTime += 1; + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); - Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, - /* down= */ GESTURES_BUTTON_NONE, - /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(3u, args.size()); + ASSERT_EQ(5u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(1), + WithRelativeMotion(0.f, 0.f))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithRelativeMotion(0.f, 0.f))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithRelativeMotion(0.f, 0.f), + WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + WithButtonState(0))); +} + +TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + // Click should still produce button press/release events + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -1290,14 +1461,14 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); @@ -1305,6 +1476,45 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } +TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + const nsecs_t gestureStartTime = 1000; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Start a move gesture at gestureStartTime + Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + + // Key presses with IME connection should cancel ongoing move gesture + nsecs_t currentTime = gestureStartTime + 100; + mFakePolicy->setIsInputMethodConnectionActive(true); + mReader->getContext()->setLastKeyDownTimestamp(currentTime); + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)))); + + // any updates in existing move gesture should be ignored + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_EQ(0u, args.size()); + + // New gesture should not be affected + currentTime += 100; + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); +} + // TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging // logic can be removed. class GestureConverterTestWithChoreographer : public GestureConverterTestBase { @@ -1321,13 +1531,14 @@ TEST_F(GestureConverterTestWithChoreographer, Move) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { @@ -1337,13 +1548,14 @@ TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { @@ -1355,65 +1567,64 @@ TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Then release the left button Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Finally release the right button Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { @@ -1425,53 +1636,53 @@ TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Move Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Release the button Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll) { @@ -1481,47 +1692,50 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -10), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(0, -10), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { @@ -1532,40 +1746,46 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-10, 0), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(-10, 0), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) { @@ -1574,21 +1794,22 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGe converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionClassification(MotionClassification::NONE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::NONE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) { @@ -1597,20 +1818,21 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGe converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON)); } @@ -1622,17 +1844,18 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificat Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5, /*dy=*/10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { @@ -1642,16 +1865,17 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxes Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5, /*dy=*/5); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0))); @@ -1668,7 +1892,8 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); // Three fake fingers should be created. We don't actually care where they are, so long as they @@ -1719,7 +1944,7 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1736,30 +1961,36 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { @@ -1770,7 +2001,8 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); // Three fake fingers should be created. We don't actually care where they are, so long as they @@ -1812,7 +2044,7 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1827,23 +2059,24 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { @@ -1853,7 +2086,8 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 10, /* dy= */ 0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(5u, args.size()); // Four fake fingers should be created. We don't actually care where they are, so long as they @@ -1916,7 +2150,7 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 5, /* dy= */ 0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1935,38 +2169,46 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(4u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { @@ -1976,50 +2218,56 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(-100, 0), WithPointerCount(1u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, 100, 0), WithPointerCount(2u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0), - WithPointerCoords(1, 80, 0), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), + WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { @@ -2029,50 +2277,56 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithCoords(-100, 0), WithPointerCount(1u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCoords(1, 100, 0), WithPointerCount(2u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.1, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0), - WithPointerCoords(1, 110, 0), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.1f, EPSILON), + WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) { @@ -2082,21 +2336,22 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGes Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) { @@ -2106,21 +2361,22 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGestur Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like scroll. Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1, /*dy=*/0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON)); } @@ -2133,27 +2389,27 @@ TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { @@ -2162,17 +2418,17 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { @@ -2182,31 +2438,35 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { @@ -2216,22 +2476,24 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { @@ -2241,7 +2503,8 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), @@ -2257,50 +2520,54 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); - - ASSERT_EQ(5u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Click) { @@ -2311,7 +2578,8 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), @@ -2322,49 +2590,56 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION), + REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); @@ -2372,21 +2647,22 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); - Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); // no events should be generated ASSERT_EQ(0u, args.size()); @@ -2395,18 +2671,21 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } -TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { - // Click should still produce button press/release events +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabledWithDelay, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), @@ -2414,48 +2693,141 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, - /* down= */ GESTURES_BUTTON_LEFT, - /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - ASSERT_EQ(2u, args.size()); + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); + + // taps before the threshold should still be ignored + currentTime += TAP_ENABLE_DELAY_NANOS.count(); + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // taps after the threshold should be recognised + currentTime += 1; + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); - Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, - /* down= */ GESTURES_BUTTON_NONE, - /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(3u, args.size()); + ASSERT_EQ(5u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(1), + WithRelativeMotion(0.f, 0.f))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithRelativeMotion(0.f, 0.f))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithRelativeMotion(0.f, 0.f), + WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + WithButtonState(0))); +} + +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + // Click should still produce button press/release events + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -2471,16 +2843,57 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick, converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMove, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + const nsecs_t gestureStartTime = 1000; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Start a move gesture at gestureStartTime + Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + + // Key presses with IME connection should cancel ongoing move gesture + nsecs_t currentTime = gestureStartTime + 100; + mFakePolicy->setIsInputMethodConnectionActive(true); + mReader->getContext()->setLastKeyDownTimestamp(currentTime); + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)))); + + // any updates in existing move gesture should be ignored + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_EQ(0u, args.size()); + + // New gesture should not be affected + currentTime += 100; + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); +} + } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5002391f61..a880a4c6bd 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -549,7 +549,7 @@ private: } } - void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {} nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); @@ -572,7 +572,8 @@ private: /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is * essentially a passthrough for notifySwitch. */ - mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask); + mLastNotifySwitch = + NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); } void pokeUserActivity(nsecs_t, int32_t, int32_t) override { @@ -836,7 +837,8 @@ TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { } TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { - NotifySwitchArgs args(/*id=*/10, /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1, + NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0, + /*switchValues=*/1, /*switchMask=*/2); mDispatcher->notifySwitch(args); @@ -1182,6 +1184,11 @@ public: mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity); } + void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) { + mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH, + shouldGlobalStylusBlockTouch); + } + void setAlpha(float alpha) { mInfo.alpha = alpha; } void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } @@ -1221,31 +1228,27 @@ public: void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - KeyEvent* consumeKey(bool handled = true) { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); - if (event == nullptr) { - ADD_FAILURE() << "Consume failed : no event"; - return nullptr; + const KeyEvent& consumeKey(bool handled = true) { + const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); + if (event.getType() != InputEventType::KEY) { + LOG(FATAL) << "Instead of key event, got " << event; } - if (event->getType() != InputEventType::KEY) { - ADD_FAILURE() << "Instead of key event, got " << *event; - return nullptr; - } - return static_cast<KeyEvent*>(event); + return static_cast<const KeyEvent&>(event); } void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { - KeyEvent* keyEvent = consumeKey(); - ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher; - ASSERT_THAT(*keyEvent, matcher); + const KeyEvent& keyEvent = consumeKey(); + ASSERT_THAT(keyEvent, matcher); } void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); + consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); + consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, @@ -1267,44 +1270,46 @@ public: void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt, std::optional<int32_t> expectedFlags = std::nullopt) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, - expectedFlags); + consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), + testing::Conditional(expectedDisplayId.has_value(), + WithDisplayId(*expectedDisplayId), testing::_), + testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags), + testing::_))); } void consumeMotionPointerDown(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | + const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | + const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, - expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId, - expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), + WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); } - void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - MotionEvent* motionEvent = consumeMotion(); - ASSERT_NE(nullptr, motionEvent); - EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked()); - EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX()); - EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY()); + void consumeMotionOutsideWithZeroedCoords() { + consumeMotionEvent( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0))); } void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { @@ -1319,21 +1324,15 @@ public: mInputReceiver->consumeCaptureEvent(hasCapture); } - const MotionEvent& consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { - MotionEvent* motionEvent = consumeMotion(); - if (nullptr == motionEvent) { - LOG(FATAL) << "Did not get a motion event, but expected " << matcher; + const MotionEvent& consumeMotionEvent( + const ::testing::Matcher<MotionEvent>& matcher = testing::_) { + const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + if (event.getType() != InputEventType::MOTION) { + LOG(FATAL) << "Instead of motion event, got " << event; } - EXPECT_THAT(*motionEvent, matcher); - return *motionEvent; - } - - void consumeEvent(InputEventType expectedEventType, int32_t expectedAction, - std::optional<int32_t> expectedDisplayId, - std::optional<int32_t> expectedFlags) { - ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; - mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId, - expectedFlags); + const auto& motionEvent = static_cast<const MotionEvent&>(event); + EXPECT_THAT(motionEvent, matcher); + return motionEvent; } void consumeDragEvent(bool isExiting, float x, float y) { @@ -1364,26 +1363,6 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) { - if (mInputReceiver == nullptr) { - return nullptr; - } - return mInputReceiver->consume(timeout, handled); - } - - MotionEvent* consumeMotion() { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - if (event == nullptr) { - ADD_FAILURE() << "Consume failed : no event"; - return nullptr; - } - if (event->getType() != InputEventType::MOTION) { - ADD_FAILURE() << "Instead of motion event, got " << *event; - return nullptr; - } - return static_cast<MotionEvent*>(event); - } - void assertNoEvents() { if (mInputReceiver == nullptr && mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { @@ -1415,6 +1394,17 @@ private: std::shared_ptr<FakeInputReceiver> mInputReceiver; static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger friend class sp<FakeWindowHandle>; + + const InputEvent& consume(std::chrono::milliseconds timeout, bool handled = true) { + if (mInputReceiver == nullptr) { + LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; + } + InputEvent* event = mInputReceiver->consume(timeout, handled); + if (event == nullptr) { + LOG(FATAL) << "Consume failed: no event"; + } + return *event; + } }; std::atomic<int32_t> FakeWindowHandle::sId{1}; @@ -1579,9 +1569,9 @@ static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A, - AMETA_NONE, currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, POLICY_FLAG_PASS_TO_USER, action, + /*flags=*/0, AKEYCODE_A, KEY_A, AMETA_NONE, currentTime); return args; } @@ -1590,9 +1580,9 @@ static NotifyKeyArgs generateSystemShortcutArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON, - currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, + AMETA_META_ON, currentTime); return args; } @@ -1601,9 +1591,9 @@ static NotifyKeyArgs generateAssistantKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT, - AMETA_NONE, currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, + KEY_ASSISTANT, AMETA_NONE, currentTime); return args; } @@ -1631,9 +1621,9 @@ static NotifyKeyArgs generateAssistantKeyArgs(int32_t action, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. - NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0, - AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, + NotifyMotionArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, source, + displayId, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, + /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties, pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -1652,7 +1642,8 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs( const PointerCaptureRequest& request) { - return NotifyPointerCaptureChangedArgs(/*id=*/0, systemTime(SYSTEM_TIME_MONOTONIC), request); + return NotifyPointerCaptureChangedArgs(InputEvent::nextId(), systemTime(SYSTEM_TIME_MONOTONIC), + request); } } // namespace @@ -3355,6 +3346,150 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) { } /** + * If stylus is down anywhere on the screen, then touches should not be delivered to windows that + * have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH. + * + * Two windows: one on the left and one on the right. + * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config. + * Stylus down on the left window, and then touch down on the right window. + * Check that the right window doesn't get touches while the stylus is down on the left window. + */ +TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusDownBlocksTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Left window", + ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 100, 100)); + + sp<FakeWindowHandle> sbtRightWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, + "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT); + sbtRightWindow->setFrame(Rect(100, 100, 200, 200)); + sbtRightWindow->setGlobalStylusBlocksTouch(true); + + mDispatcher->onWindowInfosChanged( + {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0}); + + const int32_t stylusDeviceId = 5; + const int32_t touchDeviceId = 4; + + // Stylus down in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId))); + + // Finger tap on the right window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + + // The touch should be blocked, because stylus is down somewhere else on screen! + sbtRightWindow->assertNoEvents(); + + // Continue stylus motion, and ensure it's not impacted. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId))); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_UP), WithDeviceId(stylusDeviceId))); + + // Now that the stylus gesture is done, touches should be getting delivered correctly. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153)) + .deviceId(touchDeviceId) + .build()); + sbtRightWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** + * If stylus is hovering anywhere on the screen, then touches should not be delivered to windows + * that have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH. + * + * Two windows: one on the left and one on the right. + * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config. + * Stylus hover on the left window, and then touch down on the right window. + * Check that the right window doesn't get touches while the stylus is hovering on the left window. + */ +TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusHoverBlocksTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Left window", + ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 100, 100)); + + sp<FakeWindowHandle> sbtRightWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, + "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT); + sbtRightWindow->setFrame(Rect(100, 100, 200, 200)); + sbtRightWindow->setGlobalStylusBlocksTouch(true); + + mDispatcher->onWindowInfosChanged( + {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0}); + + const int32_t stylusDeviceId = 5; + const int32_t touchDeviceId = 4; + + // Stylus hover in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId))); + + // Finger tap on the right window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + + // The touch should be blocked, because stylus is hovering somewhere else on screen! + sbtRightWindow->assertNoEvents(); + + // Continue stylus motion, and ensure it's not impacted. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId))); + + // Now that the stylus gesture is done, touches should be getting delivered correctly. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153)) + .deviceId(touchDeviceId) + .build()); + sbtRightWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** * A spy window above a window with no input channel. * Start hovering with a stylus device, and then tap with it. * Ensure spy window receives the entire sequence. @@ -3625,20 +3760,18 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { mDispatcher->waitForIdle(); - MotionEvent* motionEvent1 = window1->consumeMotion(); - ASSERT_NE(motionEvent1, nullptr); + const MotionEvent& motionEvent1 = window1->consumeMotionEvent(); window2->assertNoEvents(); - nsecs_t downTimeForWindow1 = motionEvent1->getDownTime(); - ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime()); + nsecs_t downTimeForWindow1 = motionEvent1.getDownTime(); + ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime()); // Now touch down on the window with another pointer mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})); mDispatcher->waitForIdle(); - MotionEvent* motionEvent2 = window2->consumeMotion(); - ASSERT_NE(motionEvent2, nullptr); - nsecs_t downTimeForWindow2 = motionEvent2->getDownTime(); + const MotionEvent& motionEvent2 = window2->consumeMotionEvent(); + nsecs_t downTimeForWindow2 = motionEvent2.getDownTime(); ASSERT_NE(downTimeForWindow1, downTimeForWindow2); - ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime()); + ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime()); // Now move the pointer on the second window mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})); @@ -4233,8 +4366,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { // When device reset happens, that key stream should be terminated with FLAG_CANCELED // on the app side. mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID}); - window->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, - AKEY_EVENT_FLAG_CANCELED); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED); } TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { @@ -4473,12 +4605,12 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - const MotionEvent* event = window->consumeMotion(); - EXPECT_EQ(POINTER_1_DOWN, event->getAction()); - EXPECT_EQ(70, event->getX(0)); // 50 + 20 - EXPECT_EQ(90, event->getY(0)); // 50 + 40 - EXPECT_EQ(-10, event->getX(1)); // -30 + 20 - EXPECT_EQ(-10, event->getY(1)); // -50 + 40 + const MotionEvent& event = window->consumeMotionEvent(); + EXPECT_EQ(POINTER_1_DOWN, event.getAction()); + EXPECT_EQ(70, event.getX(0)); // 50 + 20 + EXPECT_EQ(90, event.getY(0)); // 50 + 40 + EXPECT_EQ(-10, event.getX(1)); // -30 + 20 + EXPECT_EQ(-10, event.getY(1)); // -50 + 40 } /** @@ -4744,15 +4876,15 @@ TEST_F(InputDispatcherTest, WhenMultiDisplayWindowSameToken_DispatchCancelToTarg EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindowDefaultDisplay->getToken())); // windowDefaultDisplay gets cancel - MotionEvent* event = windowDefaultDisplay->consumeMotion(); - EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction()); + const MotionEvent& event = windowDefaultDisplay->consumeMotionEvent(); + EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); // The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the // coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y // coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of // SECOND_DISPLAY_ID, the x and y coordinates are 200 - EXPECT_EQ(100, event->getX(0)); - EXPECT_EQ(100, event->getY(0)); + EXPECT_EQ(100, event.getX(0)); + EXPECT_EQ(100, event.getY(0)); } /** @@ -4882,19 +5014,18 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate {PointF{150, 220}})); firstWindow->assertNoEvents(); - const MotionEvent* event = secondWindow->consumeMotion(); - ASSERT_NE(nullptr, event); - EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction()); + const MotionEvent& event = secondWindow->consumeMotionEvent(); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event.getAction()); // Ensure that the events from the "getRaw" API are in logical display coordinates. - EXPECT_EQ(300, event->getRawX(0)); - EXPECT_EQ(880, event->getRawY(0)); + EXPECT_EQ(300, event.getRawX(0)); + EXPECT_EQ(880, event.getRawY(0)); // Ensure that the x and y values are in the window's coordinate space. // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in // the logical display space. This will be the origin of the window space. - EXPECT_EQ(100, event->getX(0)); - EXPECT_EQ(80, event->getY(0)); + EXPECT_EQ(100, event.getX(0)); + EXPECT_EQ(80, event.getY(0)); } TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) { @@ -5968,8 +6099,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { motionArgs.pointerCoords[0].getX() - 10); mDispatcher->notifyMotion(motionArgs); - window->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT, - /*expectedFlags=*/0); + window->consumeMotionMove(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0); } /** @@ -6039,10 +6169,9 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); mDispatcher->notifyKey(keyArgs); - KeyEvent* event = window->consumeKey(); - ASSERT_NE(event, nullptr); + const KeyEvent& event = window->consumeKey(); - std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event); + std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event); ASSERT_NE(verified, nullptr); ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY); @@ -6083,10 +6212,9 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { ADISPLAY_ID_DEFAULT); mDispatcher->notifyMotion(motionArgs); - MotionEvent* event = window->consumeMotion(); - ASSERT_NE(event, nullptr); + const MotionEvent& event = window->consumeMotionEvent(); - std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event); + std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event); ASSERT_NE(verified, nullptr); ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION); @@ -6664,9 +6792,8 @@ protected: } void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) { - KeyEvent* event = mWindow->consumeKey(handled); - ASSERT_NE(event, nullptr) << "Did not receive key event"; - ASSERT_THAT(*event, matcher); + const KeyEvent& event = mWindow->consumeKey(handled); + ASSERT_THAT(event, matcher); } }; @@ -6897,7 +7024,7 @@ protected: mDispatcher->notifyKey(keyArgs); // Window should receive key down event. - mWindow->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, + mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0); } }; @@ -6967,10 +7094,9 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFrom GTEST_SKIP() << "Flaky test (b/270393106)"; sendAndConsumeKeyDown(/*deviceId=*/1); for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { - KeyEvent* repeatEvent = mWindow->consumeKey(); - ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + const KeyEvent& repeatEvent = mWindow->consumeKey(); EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER, - IdGenerator::getSource(repeatEvent->getId())); + IdGenerator::getSource(repeatEvent.getId())); } } @@ -6980,9 +7106,8 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent std::unordered_set<int32_t> idSet; for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { - KeyEvent* repeatEvent = mWindow->consumeKey(); - ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; - int32_t id = repeatEvent->getId(); + const KeyEvent& repeatEvent = mWindow->consumeKey(); + int32_t id = repeatEvent.getId(); EXPECT_EQ(idSet.end(), idSet.find(id)); idSet.insert(id); } @@ -7071,8 +7196,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0}); // Old focus should receive a cancel event. - windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, - AKEY_EVENT_FLAG_CANCELED); + windowInSecondary->consumeKeyUp(ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED); // Test inject a key down, should timeout because of no target window. ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher)); @@ -7550,24 +7674,21 @@ protected: void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, const std::vector<PointF>& points) { const std::string name = window->getName(); - MotionEvent* motionEvent = window->consumeMotion(); - - ASSERT_NE(nullptr, motionEvent) - << name.c_str() << ": consumer should have returned non-NULL event."; + const MotionEvent& motionEvent = + window->consumeMotionEvent(WithMotionAction(expectedAction)); - ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction)); - ASSERT_EQ(points.size(), motionEvent->getPointerCount()); + ASSERT_EQ(points.size(), motionEvent.getPointerCount()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; float expectedY = points[i].y; - EXPECT_EQ(expectedX, motionEvent->getX(i)) + EXPECT_EQ(expectedX, motionEvent.getX(i)) << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() - << ", got " << motionEvent->getX(i); - EXPECT_EQ(expectedY, motionEvent->getY(i)) + << ", got " << motionEvent.getX(i); + EXPECT_EQ(expectedY, motionEvent.getY(i)) << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() - << ", got " << motionEvent->getY(i); + << ", got " << motionEvent.getY(i); } } @@ -8380,8 +8501,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { .build())); mFocusedWindow->consumeMotionDown(); mFocusedWindow->consumeMotionUp(); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // We consumed all events, so no ANR ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertNotifyAnrWasNotCalled(); @@ -8457,8 +8577,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout // At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events. TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { tapOnFocusedWindow(); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // Receive the events, but don't respond std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(downEventSequenceNum); @@ -8590,8 +8709,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION})); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // Touch Window 2 mDispatcher->notifyMotion( @@ -9723,7 +9841,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilf // Receives cancel for first pointer after next pointer down mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); - mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + mSpyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithPointerIds({1}))); mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); mSpyWindow->assertNoEvents(); @@ -9733,13 +9851,13 @@ TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilf mDragWindow->assertNoEvents(); const MotionEvent firstFingerMoveEvent = - MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(60).y(60)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60)) .build(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + injectMotionEvent(*mDispatcher, firstFingerMoveEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; @@ -9930,8 +10048,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { .displayId(SECOND_DISPLAY_ID) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .build())); - windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, - SECOND_DISPLAY_ID, /*expectedFlag=*/0); + windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID, /*expectedFlag=*/0); // Update window again. mDispatcher->onWindowInfosChanged( {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e0a3e94aad..c6536de0d4 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2894,9 +2894,9 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertConfigureWasCalled(); mapper.assertResetWasNotCalled(); - RawEvent event{.deviceId = EVENTHUB_ID, + RawEvent event{.when = ARBITRARY_TIME, .readTime = ARBITRARY_TIME, - .when = ARBITRARY_TIME, + .deviceId = EVENTHUB_ID, .type = EV_SYN, .code = SYN_REPORT, .value = 0}; @@ -10971,7 +10971,7 @@ TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) { ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount()); } -TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) { +TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); @@ -10994,36 +10994,25 @@ TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) { ASSERT_NO_FATAL_FAILURE( mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN))); - // Reset the mapper. When the mapper is reset, the touch state is also cleared. + // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be + // preserved. Resetting should cancel the ongoing gesture. resetMapper(mapper, ARBITRARY_TIME); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); - // Move the second slot pointer, and ensure there are no events, because the touch state was - // cleared and no slots should be in use. + // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use + // the existing touch state to generate a down event. processPosition(mapper, 301, 302); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Release both fingers. - processId(mapper, INVALID_TRACKING_ID); - processSlot(mapper, FIRST_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Start a new gesture, and ensure we get a DOWN event for it. - processId(mapper, FIRST_TRACKING_ID); - processPosition(mapper, 200, 300); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f)))); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } -TEST_F(MultiTouchInputMapperTest, ResetClearsTouchStateWithNoPointersDown) { +TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); @@ -11151,66 +11140,6 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirect ASSERT_FALSE(fakePointerController->isPointerShown()); } -TEST_F(MultiTouchInputMapperTest, SimulateKernelBufferOverflow) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(ui::ROTATION_0); - prepareAxes(POSITION | ID | SLOT | PRESSURE); - MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); - - // First finger down. - processId(mapper, FIRST_TRACKING_ID); - processPosition(mapper, 100, 200); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); - - // Assume the kernel buffer overflows, and we get a SYN_DROPPED event. - // This will reset the mapper, and thus also reset the touch state. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_DROPPED, 0); - resetMapper(mapper, ARBITRARY_TIME); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); - - // Since the touch state was reset, it doesn't know which slots are active, so any movements - // are ignored. - processPosition(mapper, 101, 201); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Second finger goes down. This is the first active finger, so we get a DOWN event. - processSlot(mapper, SECOND_SLOT); - processId(mapper, SECOND_TRACKING_ID); - processPosition(mapper, 400, 500); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); - - // First slot is still ignored, only the second one is active. - processSlot(mapper, FIRST_SLOT); - processPosition(mapper, 102, 202); - processSlot(mapper, SECOND_SLOT); - processPosition(mapper, 401, 501); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); - - // Both slots up, so we get the UP event for the active pointer. - processSlot(mapper, FIRST_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSlot(mapper, SECOND_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} - // --- MultiTouchInputMapperTest_ExternalDevice --- class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 2457f7c532..193b84d313 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -17,6 +17,7 @@ #include "../PointerChoreographer.h" #include <gtest/gtest.h> +#include <deque> #include <vector> #include "FakePointerController.h" @@ -33,7 +34,9 @@ namespace { // Helpers to std::visit with lambdas. template <typename... V> -struct Visitor : V... {}; +struct Visitor : V... { + using V::operator()...; +}; template <typename... V> Visitor(V...) -> Visitor<V...>; @@ -88,14 +91,14 @@ protected: std::shared_ptr<FakePointerController> assertPointerControllerCreated( ControllerType expectedType) { - EXPECT_TRUE(mLastCreatedController) << "No PointerController was created"; - auto [type, controller] = std::move(*mLastCreatedController); + EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created"; + auto [type, controller] = std::move(mCreatedControllers.front()); EXPECT_EQ(expectedType, type); - mLastCreatedController.reset(); + mCreatedControllers.pop_front(); return controller; } - void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); } + void assertPointerControllerNotCreated() { ASSERT_TRUE(mCreatedControllers.empty()); } void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) { // Ensure that the code under test is not holding onto this PointerController. @@ -110,6 +113,12 @@ protected: "to this PointerController"; } + void assertPointerControllerNotRemoved(const std::shared_ptr<FakePointerController>& pc) { + // See assertPointerControllerRemoved above. + ASSERT_GT(pc.use_count(), 1) << "Expected PointerChoreographer to hold at least one " + "reference to this PointerController"; + } + void assertPointerDisplayIdNotified(int32_t displayId) { ASSERT_EQ(displayId, mPointerDisplayIdNotified); mPointerDisplayIdNotified.reset(); @@ -118,17 +127,15 @@ protected: void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); } private: - std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>> - mLastCreatedController; + std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>> + mCreatedControllers; std::optional<int32_t> mPointerDisplayIdNotified; std::shared_ptr<PointerControllerInterface> createPointerController( ControllerType type) override { - EXPECT_FALSE(mLastCreatedController.has_value()) - << "More than one PointerController created at a time"; std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>(); EXPECT_FALSE(pc->isPointerShown()); - mLastCreatedController = {type, pc}; + mCreatedControllers.emplace_back(type, pc); return pc; } @@ -184,33 +191,15 @@ TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { } } -TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) { +TEST_F(PointerChoreographerTest, WhenMouseIsAddedCreatesPointerController) { mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - assertPointerControllerNotCreated(); -} - -TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) { - mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); } TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) { mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); // Remove the mouse. @@ -226,34 +215,20 @@ TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerControll } TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) { - // Just adding a viewport or device should not create a PointerController. + // Just adding a viewport or device should create a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); - assertPointerControllerNotCreated(); - // After the mouse emits event, PointerController will be created and viewport will be set. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportSet(DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); } TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) { - // Without viewport information, PointerController will be created by a mouse event - // but viewport won't be set. + // Without viewport information, PointerController will be created but viewport won't be set. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportNotSet(); @@ -270,14 +245,9 @@ TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { // the PointerController. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportSet(DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); } TEST_F(PointerChoreographerTest, @@ -287,29 +257,18 @@ TEST_F(PointerChoreographerTest, mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); firstDisplayPc->assertViewportSet(DISPLAY_ID); + ASSERT_TRUE(firstDisplayPc->isPointerShown()); - // Change default mouse display. Existing PointerController should be removed. + // Change default mouse display. Existing PointerController should be removed and a new one + // should be created. mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); - assertPointerControllerNotCreated(); - // New PointerController for the new default display will be created by the motion event. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID); + ASSERT_TRUE(secondDisplayPc->isPointerShown()); } TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { @@ -317,12 +276,6 @@ TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); @@ -332,12 +285,6 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplay mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotNotified(); @@ -350,12 +297,6 @@ TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdCh mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); @@ -372,28 +313,13 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointe mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); - // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified - // before a mouse event. + // Set another viewport as a default mouse display ID. The mouse is moved to the other display. mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); - assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); assertPointerControllerRemoved(firstDisplayPc); - // After a mouse event, pointer display ID will be notified with new default mouse display. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID); } @@ -403,13 +329,6 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); @@ -428,7 +347,7 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { pc->assertPosition(110, 220); ASSERT_TRUE(pc->isPointerShown()); - // Check that x-y cooridnates, displayId and cursor position are correctly updated. + // Check that x-y coordinates, displayId and cursor position are correctly updated. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220))); } @@ -444,23 +363,8 @@ TEST_F(PointerChoreographerTest, {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId()); - - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(ANOTHER_DISPLAY_ID) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); @@ -483,7 +387,7 @@ TEST_F(PointerChoreographerTest, ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); ASSERT_TRUE(associatedMousePc->isPointerShown()); - // Check that x-y cooridnates, displayId and cursor position are correctly updated. + // Check that x-y coordinates, displayId and cursor position are correctly updated. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID), WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420))); @@ -494,13 +398,6 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); @@ -531,7 +428,7 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { pc->assertPosition(100, 200); ASSERT_FALSE(pc->isPointerShown()); - // Check x-y cooridnates, displayId and cursor position are not changed. + // Check x-y coordinates, displayId and cursor position are not changed. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE), WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -543,13 +440,6 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); ASSERT_TRUE(pc->isPointerShown()); @@ -561,6 +451,65 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { ASSERT_FALSE(pc->isPointerShown()); } +TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // A mouse is connected, and the pointer is shown. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + pc->fade(PointerControllerInterface::Transition::IMMEDIATE); + + // Add a second mouse is added, the pointer is shown again. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + ASSERT_TRUE(pc->isPointerShown()); + + // One of the mice is removed, and it does not cause the mouse pointer to fade, because + // we have one more mouse connected. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + assertPointerControllerNotRemoved(pc); + ASSERT_TRUE(pc->isPointerShown()); + + // The final mouse is removed. The pointer is removed. + mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}}); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + pc->fade(PointerControllerInterface::Transition::IMMEDIATE); + + // Adding a touchscreen device does not unfade the mouse pointer. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), + generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, + DISPLAY_ID)}}); + + ASSERT_FALSE(pc->isPointerShown()); + + // Show touches setting change does not unfade the mouse pointer. + mChoreographer.setShowTouchesEnabled(true); + + ASSERT_FALSE(pc->isPointerShown()); +} + TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) { // Disable show touches and add a touch device. mChoreographer.setShowTouchesEnabled(false); @@ -1038,25 +987,11 @@ TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) { assertPointerControllerRemoved(pc); } -TEST_F(PointerChoreographerTest, WhenTouchpadIsJustAddedDoesNotCreatePointerController) { - mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, - {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, - ADISPLAY_ID_NONE)}}); - assertPointerControllerNotCreated(); -} - -TEST_F(PointerChoreographerTest, WhenTouchpadEventOccursCreatesPointerController) { +TEST_F(PointerChoreographerTest, WhenTouchpadIsAddedCreatesPointerController) { mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); } @@ -1065,12 +1000,6 @@ TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedRemovesPointerController) {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); // Remove the touchpad. @@ -1085,15 +1014,6 @@ TEST_F(PointerChoreographerTest, SetsViewportForAssociatedTouchpad) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, DISPLAY_ID)}}); - assertPointerControllerNotCreated(); - - // After the touchpad emits event, PointerController will be created and viewport will be set. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportSet(DISPLAY_ID); } @@ -1105,12 +1025,6 @@ TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedTo {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportNotSet(); @@ -1127,32 +1041,19 @@ TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController // the PointerController. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) { - // Set one display as a default touchpad display and emit touchpad event to create - // PointerController. + // Set one display as a default touchpad display and create PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); firstDisplayPc->assertViewportSet(DISPLAY_ID); @@ -1160,13 +1061,6 @@ TEST_F(PointerChoreographerTest, mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); - // New PointerController for the new default display will be created by the motion event. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID); } @@ -1178,12 +1072,6 @@ TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); @@ -1195,12 +1083,6 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointe {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotNotified(); @@ -1215,12 +1097,6 @@ TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayI {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); @@ -1240,28 +1116,14 @@ TEST_F(PointerChoreographerTest, {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(DISPLAY_ID); // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified // before a touchpad event. mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); - assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); assertPointerControllerRemoved(firstDisplayPc); - // After a touchpad event, pointer display ID will be notified with new default mouse display. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); assertPointerControllerCreated(ControllerType::MOUSE); assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID); } @@ -1273,13 +1135,6 @@ TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); @@ -1298,7 +1153,7 @@ TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) { pc->assertPosition(110, 220); ASSERT_TRUE(pc->isPointerShown()); - // Check that x-y cooridnates, displayId and cursor position are correctly updated. + // Check that x-y coordinates, displayId and cursor position are correctly updated. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220))); } @@ -1310,13 +1165,6 @@ TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); @@ -1398,23 +1246,8 @@ TEST_F(PointerChoreographerTest, ADISPLAY_ID_NONE), generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ANOTHER_DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId()); - - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(ANOTHER_DISPLAY_ID) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); @@ -1437,7 +1270,7 @@ TEST_F(PointerChoreographerTest, ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); ASSERT_TRUE(associatedMousePc->isPointerShown()); - // Check that x-y cooridnates, displayId and cursor position are correctly updated. + // Check that x-y coordinates, displayId and cursor position are correctly updated. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID), WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420))); @@ -1450,13 +1283,6 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); @@ -1479,7 +1305,7 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { pc->assertPosition(200, 300); ASSERT_FALSE(pc->isPointerShown()); - // Check x-y cooridnates, displayId and cursor position are not changed. + // Check x-y coordinates, displayId and cursor position are not changed. mTestListener.assertNotifyMotionWasCalled( AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE), WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -1493,13 +1319,6 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(TOUCHPAD_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); ASSERT_TRUE(pc->isPointerShown()); @@ -1517,12 +1336,6 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertPointerIconNotSet(); @@ -1537,12 +1350,6 @@ TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertPointerIconNotSet(); @@ -1558,12 +1365,6 @@ TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) { mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertPointerIconNotSet(); @@ -1573,41 +1374,12 @@ TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) { pc->assertPointerIconNotSet(); } -TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForDeviceWithoutPointerController) { - // Add two devices, one with a PointerController and the other without PointerController. - mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); - mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, - {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), - generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::MOUSE); - pc->assertPointerIconNotSet(); - - // Set pointer icon for the device without PointerController. This should be ignored. - ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID, - SECOND_DEVICE_ID)); - pc->assertPointerIconNotSet(); -} - TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) { // Make sure there is a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); pc->assertCustomPointerIconNotSet(); @@ -1632,20 +1404,8 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) { {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId()); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(ANOTHER_DISPLAY_ID) - .build()); auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId()); diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index 66fdaa4520..8ba497a3ea 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -464,6 +464,53 @@ inline WithPointersMatcher WithPointers( return WithPointersMatcher(pointers); } +/// Pointer ids matcher +class WithPointerIdsMatcher { +public: + using is_gtest_matcher = void; + explicit WithPointerIdsMatcher(std::set<int32_t> pointerIds) : mPointerIds(pointerIds) {} + + bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const { + std::set<int32_t> actualPointerIds; + for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) { + const PointerProperties* properties = event.getPointerProperties(pointerIndex); + actualPointerIds.insert(properties->id); + } + + if (mPointerIds != actualPointerIds) { + *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got " + << dumpSet(actualPointerIds); + return false; + } + return true; + } + + bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const { + std::set<int32_t> actualPointerIds; + for (const PointerProperties& properties : event.pointerProperties) { + actualPointerIds.insert(properties.id); + } + + if (mPointerIds != actualPointerIds) { + *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got " + << dumpSet(actualPointerIds); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os) const { *os << "with pointer ids " << dumpSet(mPointerIds); } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer ids"; } + +private: + const std::set<int32_t> mPointerIds; +}; + +inline WithPointerIdsMatcher WithPointerIds(const std::set<int32_t /*id*/>& pointerIds) { + return WithPointerIdsMatcher(pointerIds); +} + /// Key code class WithKeyCodeMatcher { public: diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp index 0bd8fb3de3..dc5a2130e7 100644 --- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp @@ -65,6 +65,27 @@ private: std::map<int32_t /*displayId*/, InputVerifier> mVerifiers; }; +void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) { + const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + + window.setFrame(Rect(left, top, left + width, top + height)); + window.setSlippery(fdp.ConsumeBool()); + window.setDupTouchToWallpaper(fdp.ConsumeBool()); + window.setIsWallpaper(fdp.ConsumeBool()); + window.setVisible(fdp.ConsumeBool()); + window.setPreventSplitting(fdp.ConsumeBool()); + const bool isTrustedOverlay = fdp.ConsumeBool(); + window.setTrustedOverlay(isTrustedOverlay); + if (isTrustedOverlay) { + window.setSpy(fdp.ConsumeBool()); + } else { + window.setSpy(false); + } +} + } // namespace sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher, @@ -73,17 +94,9 @@ sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatch std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++); sp<FakeWindowHandle> window = - sp<FakeWindowHandle>::make(application, dispatcher, "Fake", displayId); + sp<FakeWindowHandle>::make(application, dispatcher, windowName, displayId); - const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - - window->setFrame(Rect(left, top, left + width, top + height)); - window->setSlippery(fdp.ConsumeBool()); - window->setDupTouchToWallpaper(fdp.ConsumeBool()); - window->setTrustedOverlay(fdp.ConsumeBool()); + scrambleWindow(fdp, *window); return window; } @@ -113,7 +126,14 @@ void randomizeWindows( windowsPerDisplay.erase(displayId); } }, - // Could also clone a window, change flags, reposition, etc... + // Change flags or move some of the existing windows + [&]() -> void { + for (auto& window : windows) { + if (fdp.ConsumeBool()) { + scrambleWindow(fdp, *window); + } + } + }, })(); } diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp index ef723c229c..bd2b10a149 100644 --- a/services/powermanager/WorkDuration.cpp +++ b/services/powermanager/WorkDuration.cpp @@ -25,8 +25,9 @@ namespace android::os { WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos, int64_t cpuDurationNanos, int64_t gpuDurationNanos) - : workPeriodStartTimestampNanos(startTimestampNanos), + : timestampNanos(0), actualTotalDurationNanos(totalDurationNanos), + workPeriodStartTimestampNanos(startTimestampNanos), actualCpuDurationNanos(cpuDurationNanos), actualGpuDurationNanos(gpuDurationNanos) {} diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h index 99b5b8b1b4..26a575f834 100644 --- a/services/powermanager/include/android/WorkDuration.h +++ b/services/powermanager/include/android/WorkDuration.h @@ -61,11 +61,11 @@ struct WorkDuration : AWorkDuration, android::Parcelable { return os; } - int64_t workPeriodStartTimestampNanos; + int64_t timestampNanos; int64_t actualTotalDurationNanos; + int64_t workPeriodStartTimestampNanos; int64_t actualCpuDurationNanos; int64_t actualGpuDurationNanos; - int64_t timestampNanos; }; } // namespace android::os diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 641ba9f44b..3d2cf293ed 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -29,9 +29,12 @@ #include <thread> using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::ChannelConfig; using aidl::android::hardware::power::IPower; using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::Mode; +using aidl::android::hardware::power::SessionConfig; +using aidl::android::hardware::power::SessionTag; using android::binder::Status; using namespace android; @@ -53,6 +56,14 @@ public: (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, SessionTag tag, SessionConfig* config, + std::shared_ptr<IPowerHintSession>* _aidl_return), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, + (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); + MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 11c56a8c56..0dd4dd6525 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -7,6 +7,18 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "sensorservice_flags", + package: "com.android.frameworks.sensorservice.flags", + srcs: ["senserservice_flags.aconfig"], +} + +cc_aconfig_library { + name: "sensorservice_flags_c_lib", + aconfig_declarations: "dynamic_sensors_flags", + host_supported: true, +} + cc_library { name: "libsensorservice", diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig new file mode 100644 index 0000000000..a3bd0ee483 --- /dev/null +++ b/services/sensorservice/senserservice_flags.aconfig @@ -0,0 +1,15 @@ +package: "com.android.frameworks.sensorservice.flags" + +flag { + name: "dynamic_sensor_hal_reconnect_handling" + namespace: "sensors" + description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected." + bug: "307782607" +} + +flag { + name: "sensor_device_on_dynamic_sensor_disconnected" + namespace: "sensors" + description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not." + bug: "316958439" +}
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index 5e84be1841..c71c517d94 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -40,6 +40,9 @@ public: // True if the display is secure virtual bool isSecure() const = 0; + // Sets the secure flag for the display + virtual void setSecure(bool secure) = 0; + // True if the display is virtual virtual bool isVirtual() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index eac5d97df3..c53b46140b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -74,6 +74,7 @@ public: void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; void createClientCompositionCache(uint32_t cacheSize) override; void applyDisplayBrightness(const bool applyImmediately) override; + void setSecure(bool secure) override; // Internal helpers used by chooseCompositionStrategy() using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 7e99ec2f5a..46cb95ef25 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -33,6 +33,7 @@ public: MOCK_CONST_METHOD0(getId, DisplayId()); MOCK_CONST_METHOD0(isSecure, bool()); + MOCK_METHOD1(setSecure, void(bool)); MOCK_CONST_METHOD0(isVirtual, bool()); MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t()); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 690d35f068..d907bf538d 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -74,6 +74,10 @@ bool Display::isSecure() const { return getState().isSecure; } +void Display::setSecure(bool secure) { + editState().isSecure = secure; +} + bool Display::isVirtual() const { return VirtualDisplayId::tryCast(mId).has_value(); } @@ -246,7 +250,6 @@ bool Display::chooseCompositionStrategy( } // Get any composition changes requested by the HWC device, and apply them. - std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); const bool requiresClientComposition = anyLayersRequireClientComposition(); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index a18397d8ba..91cfe5dd41 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -56,7 +56,7 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } // Do not unflatten if source crop is only moved. - if (FlagManager::getInstance().cache_if_source_crop_layer_only_moved() && + if (FlagManager::getInstance().cache_when_source_crop_layer_only_moved() && incomingLayers[i]->isSourceCropSizeEqual(*(existingLayers[i])) && incomingLayers[i]->getDifferingFields(*(existingLayers[i])) == LayerStateField::SourceCrop) { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 8b736be5e9..9e35717c95 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -148,8 +148,7 @@ public: MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); - MOCK_METHOD(status_t, notifyExpectedPresentIfRequired, - (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>)); + MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index bf7ed87443..30ba9e439e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -4094,7 +4094,11 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS }; TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { - mOutput.mState.isSecure = true; + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4108,7 +4112,11 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLa } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { - mOutput.mState.isSecure = true; + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); @@ -4130,7 +4138,11 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) { - mOutput.mState.isSecure = true; + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4143,7 +4155,11 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { - mOutput.mState.isSecure = true; + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4222,6 +4238,7 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { GenerateClientCompositionRequestsTest() { mOutput.mState.needsFiltering = false; + mOutput.mState.isProtected = true; mOutput.setDisplayColorProfileForTest( std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); @@ -4246,6 +4263,7 @@ struct GenerateClientCompositionRequestsTest_ThreeLayers mOutput.mState.displaySpace.setOrientation(kDisplayOrientation); mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = false; + mOutput.mState.isProtected = true; for (size_t i = 0; i < mLayers.size(); i++) { mLayers[i].mOutputLayerState.clearClientTarget = false; @@ -4708,7 +4726,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4721,7 +4739,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4734,7 +4752,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4912,7 +4930,7 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq Region(Rect(0, 0, 1000, 1000)), false, /* needs filtering */ true, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4931,7 +4949,7 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq Region(Rect(1000, 0, 2000, 1000)), false, /* needs filtering */ true, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 763b998b3d..d2eff75fb6 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -244,7 +244,7 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { TEST_F(FlattenerTest, unflattenLayers_onlySourceCropMoved) { SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: - cache_if_source_crop_layer_only_moved, + cache_when_source_crop_layer_only_moved, true); auto& layerState1 = mTestLayers[0]->layerState; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 950b05e1da..7b0aad7316 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -222,7 +222,6 @@ bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { mPendingModeOpt = std::move(desiredMode); - mIsModeSetPending = true; const auto& mode = *mPendingModeOpt->mode.modePtr; @@ -235,9 +234,22 @@ bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode return true; } -void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) { - setActiveMode(modeId, vsyncRate, renderFps); - mIsModeSetPending = false; +auto DisplayDevice::finalizeModeChange() -> ModeChange { + if (!mPendingModeOpt) return NoModeChange{"No pending mode"}; + + auto pendingMode = *std::exchange(mPendingModeOpt, std::nullopt); + auto& pendingModePtr = pendingMode.mode.modePtr; + + if (!mRefreshRateSelector->displayModes().contains(pendingModePtr->getId())) { + return NoModeChange{"Unknown pending mode"}; + } + + if (getActiveMode().modePtr->getResolution() != pendingModePtr->getResolution()) { + return ResolutionChange{std::move(pendingMode)}; + } + + setActiveMode(pendingModePtr->getId(), pendingModePtr->getVsyncRate(), pendingMode.mode.fps); + return RefreshRateChange{std::move(pendingMode)}; } nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { @@ -353,6 +365,10 @@ bool DisplayDevice::isSecure() const { return mCompositionDisplay->isSecure(); } +void DisplayDevice::setSecure(bool secure) { + mCompositionDisplay->setSecure(secure); +} + const Rect DisplayDevice::getBounds() const { return mCompositionDisplay->getState().displaySpace.getBoundsAsRect(); } @@ -571,10 +587,14 @@ auto DisplayDevice::getDesiredMode() const -> DisplayModeRequestOpt { return mDesiredModeOpt; } -void DisplayDevice::clearDesiredMode() { - std::scoped_lock lock(mDesiredModeLock); - mDesiredModeOpt.reset(); - mHasDesiredModeTrace = false; +auto DisplayDevice::takeDesiredMode() -> DisplayModeRequestOpt { + DisplayModeRequestOpt desiredModeOpt; + { + std::scoped_lock lock(mDesiredModeLock); + std::swap(mDesiredModeOpt, desiredModeOpt); + mHasDesiredModeTrace = false; + } + return desiredModeOpt; } void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index ac390cb8ff..4c09ffdf44 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -19,6 +19,7 @@ #include <memory> #include <string> #include <unordered_map> +#include <variant> #include <android-base/thread_annotations.h> #include <android/native_window.h> @@ -95,6 +96,7 @@ public: // isSecure indicates whether this display can be trusted to display // secure surfaces. bool isSecure() const; + void setSecure(bool secure); int getWidth() const; int getHeight() const; @@ -194,12 +196,11 @@ public: using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock); - void clearDesiredMode() EXCLUDES(mDesiredModeLock); + DisplayModeRequestOpt takeDesiredMode() EXCLUDES(mDesiredModeLock); - DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) { - return mPendingModeOpt; + bool isModeSetPending() const REQUIRES(kMainThreadContext) { + return mPendingModeOpt.has_value(); } - bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; } scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) { return mRefreshRateSelector->getActiveMode(); @@ -211,8 +212,25 @@ public: hal::VsyncPeriodChangeTimeline& outTimeline) REQUIRES(kMainThreadContext); - void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps) - REQUIRES(kMainThreadContext); + struct NoModeChange { + const char* reason; + }; + + struct ResolutionChange { + display::DisplayModeRequest activeMode; + }; + + struct RefreshRateChange { + display::DisplayModeRequest activeMode; + }; + + using ModeChange = std::variant<NoModeChange, ResolutionChange, RefreshRateChange>; + + // Clears the pending DisplayModeRequest, and returns the ModeChange that occurred. If it was a + // RefreshRateChange, the pending mode becomes the active mode. If it was a ResolutionChange, + // the caller is responsible for resizing the framebuffer to match the active resolution by + // recreating the DisplayDevice. + ModeChange finalizeModeChange() REQUIRES(kMainThreadContext); scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; } @@ -249,6 +267,8 @@ public: void dump(utils::Dumper&) const; private: + friend class TestableSurfaceFlinger; + template <size_t N> inline std::string concatId(const char (&str)[N]) const { return std::string(ftl::Concat(str, ' ', getId().value).str()); @@ -299,12 +319,15 @@ private: // This parameter is only used for hdr/sdr ratio overlay float mHdrSdrRatio = 1.0f; + // A DisplayModeRequest flows through three states: desired, pending, and active. Requests + // within a frame are merged into a single desired request. Unless cleared, the request is + // relayed to HWC on the next frame, and becomes pending. The mode becomes active once HWC + // signals the present fence to confirm the mode set. mutable std::mutex mDesiredModeLock; DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock); TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock); DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext); - bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false; }; struct DisplayDeviceState { diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index c25f9ddd12..64a8ae7fcd 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -271,7 +271,10 @@ AidlComposer::AidlComposer(const std::string& serviceName) { } } } - + if (getLayerLifecycleBatchCommand()) { + mEnableLayerCommandBatchingFlag = + FlagManager::getInstance().enable_layer_command_batching(); + } ALOGI("Loaded AIDL composer3 HAL service"); } @@ -288,7 +291,7 @@ bool AidlComposer::isSupported(OptionalFeature feature) const { } } -bool AidlComposer::getDisplayConfigurationsSupported() const { +bool AidlComposer::isVrrSupported() const { return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config(); } @@ -407,25 +410,58 @@ Error AidlComposer::acceptDisplayChanges(Display display) { Error AidlComposer::createLayer(Display display, Layer* outLayer) { int64_t layer; - const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display), - kMaxLayerBufferCount, &layer); - if (!status.isOk()) { - ALOGE("createLayer failed %s", status.getDescription().c_str()); - return static_cast<Error>(status.getServiceSpecificError()); + Error error = Error::NONE; + if (!mEnableLayerCommandBatchingFlag) { + const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display), + kMaxLayerBufferCount, &layer); + if (!status.isOk()) { + ALOGE("createLayer failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + } else { + // generate a unique layerID. map in AidlComposer with <SF_layerID, HWC_layerID> + // Add this as a new displayCommand in execute command. + // return the SF generated layerID instead of calling HWC + layer = mLayerID++; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerLifecycleBatchCommandType(translate<int64_t>(display), + translate<int64_t>(layer), + LayerLifecycleBatchCommandType::CREATE); + writer->get().setNewBufferSlotCount(translate<int64_t>(display), + translate<int64_t>(layer), kMaxLayerBufferCount); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); } - *outLayer = translate<Layer>(layer); - return Error::NONE; + return error; } Error AidlComposer::destroyLayer(Display display, Layer layer) { - const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display), - translate<int64_t>(layer)); - if (!status.isOk()) { - ALOGE("destroyLayer failed %s", status.getDescription().c_str()); - return static_cast<Error>(status.getServiceSpecificError()); + Error error = Error::NONE; + if (!mEnableLayerCommandBatchingFlag) { + const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display), + translate<int64_t>(layer)); + if (!status.isOk()) { + ALOGE("destroyLayer failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + } else { + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get() + .setLayerLifecycleBatchCommandType(translate<int64_t>(display), + translate<int64_t>(layer), + LayerLifecycleBatchCommandType::DESTROY); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); } - return Error::NONE; + + return error; } Error AidlComposer::getActiveConfig(Display display, Config* outConfig) { @@ -591,6 +627,13 @@ Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp return Error::NONE; } +bool AidlComposer::getLayerLifecycleBatchCommand() { + std::vector<Capability> capabilities = getCapabilities(); + bool hasCapability = std::find(capabilities.begin(), capabilities.end(), + Capability::LAYER_LIFECYCLE_BATCH_COMMAND) != capabilities.end(); + return hasCapability; +} + Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) { const auto status = mAidlComposerClient->getOverlaySupport(outProperties); if (!status.isOk()) { diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 51ac1f5e6a..ea0e53a202 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -66,7 +66,7 @@ public: ~AidlComposer() override; bool isSupported(OptionalFeature) const; - bool getDisplayConfigurationsSupported() const; + bool isVrrSupported() const; std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() override; @@ -262,7 +262,7 @@ private: void removeDisplay(Display) EXCLUDES(mMutex); void addReader(Display) REQUIRES(mMutex); void removeReader(Display) REQUIRES(mMutex); - + bool getLayerLifecycleBatchCommand(); bool hasMultiThreadedPresentSupport(Display); // 64KiB minus a small space for metadata such as read/write pointers @@ -293,6 +293,8 @@ private: ftl::SharedMutex mMutex; int32_t mComposerInterfaceVersion = 1; + bool mEnableLayerCommandBatchingFlag = false; + std::atomic<int64_t> mLayerID = 1; // Buffer slots for layers are cleared by setting the slot buffer to this buffer. sp<GraphicBuffer> mClearSlotBuffer; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 1a24222af3..bc067a0e5d 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -105,7 +105,7 @@ public: }; virtual bool isSupported(OptionalFeature) const = 0; - virtual bool getDisplayConfigurationsSupported() const = 0; + virtual bool isVrrSupported() const = 0; virtual std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() = 0; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 24a9e22a2b..704ece516d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -165,8 +165,7 @@ Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Compo auto intError = mComposer.getChangedCompositionTypes( mId, &layerIds, &types); uint32_t numElements = layerIds.size(); - auto error = static_cast<Error>(intError); - error = static_cast<Error>(intError); + const auto error = static_cast<Error>(intError); if (error != Error::NONE) { return error; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 6b67865bdb..cf1c3c10b7 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -15,6 +15,7 @@ */ // TODO(b/129481165): remove the #pragma below and fix conversion issues +#include <chrono> #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -76,61 +77,10 @@ using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::VrrConfig; +using namespace std::string_literals; namespace hal = android::hardware::graphics::composer::hal; -namespace { -bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime, - android::TimePoint lastExpectedPresentTimestamp, - android::Fps lastFrameInterval, android::Period timeout, - android::Duration threshold) { - if (lastFrameInterval.getPeriodNsecs() == 0) { - return false; - } - - const auto expectedPresentTimeDeltaNs = - expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); - - if (expectedPresentTimeDeltaNs > timeout.ns()) { - return false; - } - - const auto expectedPresentPeriods = static_cast<nsecs_t>( - std::round(static_cast<float>(expectedPresentTimeDeltaNs) / - static_cast<float>(lastFrameInterval.getPeriodNsecs()))); - const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; - const auto calculatedExpectedPresentTimeNs = - lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; - const auto presentTimeDelta = - std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); - return presentTimeDelta < threshold.ns(); -} - -bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime, - android::TimePoint lastExpectedPresentTimestamp, - std::optional<android::Period> timeoutOpt, - android::Duration threshold) { - if (!timeoutOpt) { - // Always within timeout if timeoutOpt is absent and don't send hint - // for the timeout - return true; - } - - if (timeoutOpt->ns() == 0) { - // Always outside timeout if timeoutOpt is 0 and always send - // the hint for the timeout. - return false; - } - - if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { - return true; - } - - // Check if within the threshold as it can be just outside the timeout - return std::abs(expectedPresentTime.ns() - - (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); -} -} // namespace - namespace android { HWComposer::~HWComposer() = default; @@ -141,7 +91,8 @@ HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)), mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))), mUpdateDeviceProductInfoOnHotplugReconnect( - sysprop::update_device_product_info_on_hotplug_reconnect(false)) {} + sysprop::update_device_product_info_on_hotplug_reconnect(false)), + mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, false)) {} HWComposer::HWComposer(const std::string& composerServiceName) : HWComposer(Hwc2::Composer::create(composerServiceName)) {} @@ -321,7 +272,7 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId d const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId(); - if (mComposer->getDisplayConfigurationsSupported()) { + if (mComposer->isVrrSupported()) { return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs); } @@ -351,6 +302,10 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigura hwcMode.dpiY = config.dpi->y; } + if (!mEnableVrrTimeout) { + hwcMode.vrrConfig->notifyExpectedPresentConfig = {}; + } + modes.push_back(hwcMode); } @@ -538,13 +493,6 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - { - std::scoped_lock lock{displayData.expectedPresentLock}; - if (expectedPresentTime > displayData.lastExpectedPresentTimestamp.ns()) { - displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime); - } - } - ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); if (canSkipValidate) { sp<Fence> outPresentFence = Fence::NO_FENCE; @@ -939,55 +887,15 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId return NO_ERROR; } -status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, - Period vsyncPeriod, - TimePoint expectedPresentTime, - Fps frameInterval, - std::optional<Period> timeoutOpt) { +status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId, + TimePoint expectedPresentTime, Fps frameInterval) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - auto& displayData = mDisplayData[displayId]; - if (!displayData.hwcDisplay) { - // Display setup has not completed yet - return BAD_INDEX; - } - { - std::scoped_lock lock{displayData.expectedPresentLock}; - const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp; - const auto lastFrameInterval = displayData.lastFrameInterval; - displayData.lastFrameInterval = frameInterval; - const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); - - const constexpr nsecs_t kOneSecondNs = - std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); - const bool frameIntervalIsOnCadence = - isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, - lastFrameInterval, - Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 - ? timeoutOpt->ns() - : kOneSecondNs), - threshold); - - const bool expectedPresentWithinTimeout = - isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, - timeoutOpt, threshold); - - using fps_approx_ops::operator!=; - if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { - displayData.lastExpectedPresentTimestamp = expectedPresentTime; - } - - if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { - return NO_ERROR; - } - - displayData.lastExpectedPresentTimestamp = expectedPresentTime; - } - ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__, - expectedPresentTime, frameInterval.getPeriodNsecs()); - const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(), + ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__, + ticks<std::milli, float>(expectedPresentTime - TimePoint::now()), + ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs()))); + const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(), expectedPresentTime.ns(), frameInterval.getPeriodNsecs()); - if (error != hal::Error::NONE) { ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); return INVALID_OPERATION; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index af62731a41..fb32ff45e1 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -303,10 +303,8 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; - virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, - TimePoint expectedPresentTime, - Fps frameInterval, - std::optional<Period> timeoutOpt) = 0; + virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, + Fps frameInterval) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -466,9 +464,8 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; - status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, - TimePoint expectedPresentTime, Fps frameInterval, - std::optional<Period> timeoutOpt) override; + status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, + Fps frameInterval) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -502,11 +499,6 @@ private: sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires nsecs_t lastPresentTimestamp = 0; - std::mutex expectedPresentLock; - TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) = - TimePoint::fromNs(0); - Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0); - std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; bool validateWasSkipped; @@ -551,6 +543,7 @@ private: const size_t mMaxVirtualDisplayDimension; const bool mUpdateDeviceProductInfoOnHotplugReconnect; + bool mEnableVrrTimeout; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index 31c2833229..e3d962237b 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -169,10 +169,8 @@ inline std::string to_string( out << "}, "; out << "notifyExpectedPresentConfig={"; if (vrrConfig->notifyExpectedPresentConfig) { - out << "notifyExpectedPresentHeadsUpNs=" - << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentHeadsUpNs - << ", notifyExpectedPresentTimeoutNs=" - << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs; + out << "headsUpNs=" << vrrConfig->notifyExpectedPresentConfig->headsUpNs + << ", timeoutNs=" << vrrConfig->notifyExpectedPresentConfig->timeoutNs; } out << "}}"; return out.str(); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 5f1d5f8164..c4ff9cc6be 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -276,8 +276,8 @@ bool HidlComposer::isSupported(OptionalFeature feature) const { } } -bool HidlComposer::getDisplayConfigurationsSupported() const { - // getDisplayConfigurations is not supported on the HIDL composer. +bool HidlComposer::isVrrSupported() const { + // VRR is not supported on the HIDL composer. return false; }; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index c768d2758d..d78bfb7c6b 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -167,7 +167,7 @@ public: ~HidlComposer() override; bool isSupported(OptionalFeature) const; - bool getDisplayConfigurationsSupported() const; + bool isVrrSupported() const; std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index dd228b4523..a0c943ba72 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -31,10 +31,6 @@ #include <utils/Mutex.h> #include <utils/Trace.h> -#include <aidl/android/hardware/power/IPower.h> -#include <aidl/android/hardware/power/IPowerHintSession.h> -#include <aidl/android/hardware/power/WorkDuration.h> - #include <binder/IServiceManager.h> #include "../SurfaceFlingerProperties.h" diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 0276e44986..bbe51cc09d 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -25,9 +25,14 @@ #include <ui/FenceTime.h> #include <utils/Mutex.h> +// FMQ library in IPower does questionable conversions +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> -#include <compositionengine/impl/OutputCompositionState.h> #include <powermanager/PowerHalController.h> +#pragma clang diagnostic pop + +#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Time.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 803299cf6f..d0e2d7a451 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -390,6 +390,22 @@ void SurfaceFrame::setGpuComposition() { mGpuComposition = true; } +// TODO(b/316171339): migrate from perfetto side +bool SurfaceFrame::isSelfJanky() const { + int32_t jankType = getJankType().value_or(JankType::None); + + if (jankType == JankType::None) { + return false; + } + + int32_t jankBitmask = JankType::AppDeadlineMissed | JankType::Unknown; + if (jankType & jankBitmask) { + return true; + } + + return false; +} + std::optional<int32_t> SurfaceFrame::getJankType() const { std::scoped_lock lock(mMutex); if (mPresentState == PresentState::Dropped) { @@ -1113,20 +1129,23 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, } void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const { + nsecs_t previousPredictionPresentTime) const { nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0; const constexpr float kThresh = 0.5f; const constexpr float kRange = 1.5f; for (auto& surfaceFrame : mSurfaceFrames) { - if (previousActualPresentTime != 0 && - static_cast<float>(mSurfaceFlingerActuals.presentTime - previousActualPresentTime) >= + if (previousPredictionPresentTime != 0 && + static_cast<float>(mSurfaceFlingerPredictions.presentTime - + previousPredictionPresentTime) >= static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange && static_cast<float>(surfaceFrame->getPredictions().presentTime) <= - (static_cast<float>(mSurfaceFlingerActuals.presentTime) - + (static_cast<float>(mSurfaceFlingerPredictions.presentTime) - kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && static_cast<float>(surfaceFrame->getPredictions().presentTime) >= - (static_cast<float>(previousActualPresentTime) - - kThresh * static_cast<float>(mRenderRate.getPeriodNsecs()))) { + (static_cast<float>(previousPredictionPresentTime) - + kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && + // sf skipped frame is not considered if app is self janked + !surfaceFrame->isSelfJanky()) { skippedFrameStartTime = surfaceFrame->getPredictions().endTime; skippedFramePresentTime = surfaceFrame->getPredictions().presentTime; break; @@ -1215,18 +1234,18 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, }); } -void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const { +nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + nsecs_t previousPredictionPresentTime) const { if (mSurfaceFrames.empty()) { // We don't want to trace display frames without any surface frames updates as this cannot // be janky - return; + return previousPredictionPresentTime; } if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // DisplayFrame should not have an invalid token. ALOGE("Cannot trace DisplayFrame with invalid token"); - return; + return previousPredictionPresentTime; } if (mPredictionState == PredictionState::Valid) { @@ -1241,8 +1260,9 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBoo } if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) { - addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime); + addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime); } + return mSurfaceFlingerPredictions.presentTime; } float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { @@ -1333,8 +1353,9 @@ void FrameTimeline::flushPendingPresentFences() { const auto& pendingPresentFence = *mPendingPresentFences.begin(); const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; auto& displayFrame = pendingPresentFence.second; - displayFrame->onPresent(signalTime, mPreviousPresentTime); - displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime); + displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime); mPendingPresentFences.erase(mPendingPresentFences.begin()); } @@ -1349,9 +1370,10 @@ void FrameTimeline::flushPendingPresentFences() { } auto& displayFrame = pendingPresentFence.second; - displayFrame->onPresent(signalTime, mPreviousPresentTime); - displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime); - mPreviousPresentTime = signalTime; + displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime); + mPreviousActualPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); --i; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index b5047a3467..a76f7d477a 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -165,6 +165,8 @@ public: TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode); ~SurfaceFrame() = default; + bool isSelfJanky() const; + // Returns std::nullopt if the frame hasn't been classified yet. // Used by both SF and FrameTimeline. std::optional<int32_t> getJankType() const; @@ -381,8 +383,8 @@ public: // Emits a packet for perfetto tracing. The function body will be executed only if tracing // is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME // and SYSTEM_TIME_MONOTONIC. - void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const; + nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + nsecs_t previousPredictionPresentTime) const; // Sets the token, vsyncPeriod, predictions and SF start time. void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); @@ -508,7 +510,8 @@ private: uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; - nsecs_t mPreviousPresentTime = 0; + nsecs_t mPreviousActualPresentTime = 0; + nsecs_t mPreviousPredictionPresentTime = 0; const JankClassificationThresholds mJankClassificationThresholds; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index 1e5a6fbd1e..821ac0cf88 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -190,8 +190,12 @@ bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const { return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID; } -LayerHierarchyBuilder::LayerHierarchyBuilder( - const std::vector<std::unique_ptr<RequestedLayerState>>& layers) { +void LayerHierarchyBuilder::init(const std::vector<std::unique_ptr<RequestedLayerState>>& layers) { + mLayerIdToHierarchy.clear(); + mHierarchies.clear(); + mRoot = nullptr; + mOffscreenRoot = nullptr; + mHierarchies.reserve(layers.size()); mLayerIdToHierarchy.reserve(layers.size()); for (auto& layer : layers) { @@ -202,6 +206,7 @@ LayerHierarchyBuilder::LayerHierarchyBuilder( onLayerAdded(layer.get()); } detachHierarchyFromRelativeParent(&mOffscreenRoot); + mInitialized = true; } void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) { @@ -332,7 +337,7 @@ void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) { } } -void LayerHierarchyBuilder::update( +void LayerHierarchyBuilder::doUpdate( const std::vector<std::unique_ptr<RequestedLayerState>>& layers, const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) { // rebuild map @@ -381,6 +386,32 @@ void LayerHierarchyBuilder::update( attachHierarchyToRelativeParent(&mRoot); } +void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) { + if (!mInitialized) { + ATRACE_NAME("LayerHierarchyBuilder:init"); + init(layerLifecycleManager.getLayers()); + } else if (layerLifecycleManager.getGlobalChanges().test( + RequestedLayerState::Changes::Hierarchy)) { + ATRACE_NAME("LayerHierarchyBuilder:update"); + doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers()); + } else { + return; // nothing to do + } + + uint32_t invalidRelativeRoot; + bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot); + while (hasRelZLoop) { + ATRACE_NAME("FixRelZLoop"); + TransactionTraceWriter::getInstance().invoke("relz_loop_detected", + /*overwrite=*/false); + layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); + // reinitialize the hierarchy with the updated layer data + init(layerLifecycleManager.getLayers()); + // check if we have any remaining loops + hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot); + } +} + const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const { return mRoot; } diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index ba2e262baf..a1c73c38b0 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -17,6 +17,7 @@ #pragma once #include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerLifecycleManager.h" #include "RequestedLayerState.h" #include "ftl/small_vector.h" @@ -197,9 +198,8 @@ private: // hierarchy from a list of RequestedLayerState and associated change flags. class LayerHierarchyBuilder { public: - LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&); - void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers, - const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers); + LayerHierarchyBuilder() = default; + void update(LayerLifecycleManager& layerLifecycleManager); LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const; const LayerHierarchy& getHierarchy() const; const LayerHierarchy& getOffscreenHierarchy() const; @@ -213,14 +213,18 @@ private: void detachFromRelativeParent(LayerHierarchy*); void attachHierarchyToRelativeParent(LayerHierarchy*); void detachHierarchyFromRelativeParent(LayerHierarchy*); - + void init(const std::vector<std::unique_ptr<RequestedLayerState>>&); + void doUpdate(const std::vector<std::unique_ptr<RequestedLayerState>>& layers, + const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers); void onLayerDestroyed(RequestedLayerState* layer); void updateMirrorLayer(RequestedLayerState* layer); LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true); + std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy; std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies; LayerHierarchy mRoot{nullptr}; LayerHierarchy mOffscreenRoot{nullptr}; + bool mInitialized = false; }; } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 693a357de5..6e862b4a1e 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -100,6 +100,11 @@ std::string toString(const DisplayEventReceiver::Event& event) { case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: return StringPrintf("ModeChanged{displayId=%s, modeId=%u}", to_string(event.header.displayId).c_str(), event.modeChange.modeId); + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}", + to_string(event.header.displayId).c_str(), + event.hdcpLevelsChange.connectedLevel, + event.hdcpLevelsChange.maxLevel); default: return "Event{}"; } @@ -170,6 +175,20 @@ DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId di }}; } +DisplayEventReceiver::Event makeHdcpLevelsChange(PhysicalDisplayId displayId, + int32_t connectedLevel, int32_t maxLevel) { + return DisplayEventReceiver::Event{ + .header = + DisplayEventReceiver::Event::Header{ + .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, + .displayId = displayId, + .timestamp = systemTime(), + }, + .hdcpLevelsChange.connectedLevel = connectedLevel, + .hdcpLevelsChange.maxLevel = maxLevel, + }; +} + } // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, @@ -301,7 +320,7 @@ void EventThread::setDuration(std::chrono::nanoseconds workDuration, mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); } sp<EventThreadConnection> EventThread::createEventConnection( @@ -442,6 +461,14 @@ void EventThread::onFrameRateOverridesChanged(PhysicalDisplayId displayId, mCondition.notify_all(); } +void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) { + std::lock_guard<std::mutex> lock(mMutex); + + mPendingEvents.push_back(makeHdcpLevelsChange(displayId, connectedLevel, maxLevel)); + mCondition.notify_all(); +} + void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -501,7 +528,7 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { const auto scheduleResult = mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback"); } else { mVsyncRegistration.cancel(); @@ -757,7 +784,7 @@ scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal( if (reschedule) { mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); } return oldRegistration; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 7842318e2e..8970103a7c 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -131,6 +131,9 @@ public: const sp<EventThreadConnection>& connection) const = 0; virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0; + + virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) = 0; }; struct IEventThreadCallback { @@ -177,6 +180,9 @@ public: void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex); + void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) override; + private: friend EventThreadTest; diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 18c0a696d5..cf8b3bfdbd 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -125,7 +125,7 @@ std::unique_ptr<scheduler::VSyncCallbackRegistration> MessageQueue::onNewVsyncSc mVsync.scheduledFrameTime = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } return oldRegistration; } @@ -143,7 +143,7 @@ void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { mVsync.scheduledFrameTime = mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } void MessageQueue::waitMessage() { @@ -196,7 +196,7 @@ void MessageQueue::scheduleFrame() { mVsync.scheduledFrameTime = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 6a85788623..bfc47e67a6 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -412,6 +412,17 @@ void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDis thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); } +void Scheduler::onHdcpLevelsChanged(ConnectionHandle handle, PhysicalDisplayId displayId, + int32_t connectedLevel, int32_t maxLevel) { + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + thread->onHdcpLevelsChanged(displayId, connectedLevel, maxLevel); +} + void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { { std::lock_guard<std::mutex> lock(mPolicyLock); @@ -600,8 +611,10 @@ Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id, const Display& display = *displayOpt; const nsecs_t threshold = display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2; - const nsecs_t nextVsyncTime = display.schedulePtr->getTracker().nextAnticipatedVSyncTimeFrom( - currentExpectedPresentTime.ns() + threshold); + const nsecs_t nextVsyncTime = + display.schedulePtr->getTracker() + .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold, + currentExpectedPresentTime.ns()); return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns()); } @@ -630,6 +643,7 @@ bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp, } void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) { + ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str()); const auto scheduleOpt = (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) { return display.powerMode == hal::PowerMode::OFF @@ -640,7 +654,8 @@ void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> if (!scheduleOpt) return; const auto& schedule = scheduleOpt->get(); - if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) { + const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence)); + if (needMoreSignals) { schedule->enableHardwareVsync(); } else { constexpr bool kDisallow = false; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index ce585c624a..a29d153516 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -173,6 +173,8 @@ public: void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId) EXCLUDES(mConnectionsLock); + void onHdcpLevelsChanged(ConnectionHandle, PhysicalDisplayId, int32_t, int32_t); + // Modifies work duration in the event thread. void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index c3a952f689..f978016c9e 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -84,8 +84,8 @@ public: * able to provide the ready-by time (deadline) on the callback. * For internal clients, we don't need to add additional padding, so * readyDuration will typically be 0. - * @earliestVsync: The targeted display time. This will be snapped to the closest - * predicted vsync time after earliestVsync. + * @lastVsync: The targeted display time. This will be snapped to the closest + * predicted vsync time after lastVsync. * * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. @@ -93,11 +93,11 @@ public: struct ScheduleTiming { nsecs_t workDuration = 0; nsecs_t readyDuration = 0; - nsecs_t earliestVsync = 0; + nsecs_t lastVsync = 0; bool operator==(const ScheduleTiming& other) const { return workDuration == other.workDuration && readyDuration == other.readyDuration && - earliestVsync == other.earliestVsync; + lastVsync == other.lastVsync; } bool operator!=(const ScheduleTiming& other) const { return !(*this == other); } @@ -109,12 +109,12 @@ public: * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. * - * The caller designates the earliest vsync event that should be targeted by the earliestVsync + * The caller designates the earliest vsync event that should be targeted by the lastVsync * parameter. * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where - * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ). + * predictedVsync is the first vsync event time where ( predictedVsync >= lastVsync ). * - * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has + * If (workDuration + readyDuration - lastVsync) is in the past, or if a callback has * already been dispatched for the predictedVsync, an error will be returned. * * It is valid to reschedule a callback to a different time. diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index ef30887037..5cb0ffbfb7 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -45,8 +45,11 @@ nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime, nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now, const VSyncDispatch::ScheduleTiming& timing) { - const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( - std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); + const auto nextVsyncTime = + tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, + now + timing.workDuration + + timing.readyDuration), + timing.lastVsync); return getExpectedCallbackTime(nextVsyncTime, timing); } @@ -93,8 +96,11 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) { - auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( - std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); + auto nextVsyncTime = + tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, + now + timing.workDuration + + timing.readyDuration), + timing.lastVsync); auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; bool const wouldSkipAVsyncTarget = @@ -139,11 +145,13 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, bool const nextVsyncTooClose = mLastDispatchTime && (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod; if (alreadyDispatchedForVsync) { - return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance); + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance, + *mLastDispatchTime); } if (nextVsyncTooClose) { - return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod); + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod, + *mLastDispatchTime + currentPeriod); } return nextVsyncTime; @@ -160,11 +168,12 @@ void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { } const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration; - const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync); + const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.lastVsync); const auto nextVsyncTime = adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/ - tracker.nextAnticipatedVSyncTimeFrom(earliestVsync)); + tracker.nextAnticipatedVSyncTimeFrom(earliestVsync, + mScheduleTiming.lastVsync)); const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration; const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration; @@ -214,10 +223,10 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(), mRunning ? "(in callback function)" : "", armedInfo.c_str()); StringAppendF(&result, - "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative " + "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative " "to now\n", mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f, - (mScheduleTiming.earliestVsync - systemTime()) / 1e6f); + (mScheduleTiming.lastVsync - systemTime()) / 1e6f); if (mLastDispatchTime) { StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n", @@ -237,6 +246,7 @@ VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() { std::lock_guard lock(mMutex); + mRunning = false; cancelTimer(); for (auto& [_, entry] : mCallbacks) { ALOGE("Forgot to unregister a callback on VSyncDispatch!"); @@ -305,6 +315,10 @@ void VSyncDispatchTimerQueue::timerCallback() { std::vector<Invocation> invocations; { std::lock_guard lock(mMutex); + if (!mRunning) { + ALOGD("TimerQueue is not running. Skipping callback."); + return; + } auto const now = mTimeKeeper->now(); mLastTimerCallback = now; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index e0fb8f9f86..3d08410df7 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -148,6 +148,10 @@ private: std::mutex mutable mMutex; + // During VSyncDispatchTimerQueue deconstruction, skip timerCallback to + // avoid crash + bool mRunning = true; + static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VsyncSchedule::TrackerPtr mTracker; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 7379a4605d..28e35de465 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -88,6 +88,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { + ATRACE_FORMAT_INSTANT("timestamp is not aligned with model"); return false; } @@ -98,6 +99,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod(); if (distancePercent < kOutlierTolerancePercent) { // duplicate timestamp + ATRACE_FORMAT_INSTANT("duplicate timestamp"); return false; } return true; @@ -126,6 +128,8 @@ Period VSyncPredictor::minFramePeriodLocked() const { } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { + ATRACE_CALL(); + std::lock_guard lock(mMutex); if (!validate(timestamp)) { @@ -144,6 +148,8 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { } else { mKnownTimestamp = timestamp; } + ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago", + (systemTime() - *mKnownTimestamp) / 1e6f); return false; } @@ -247,7 +253,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { } auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence { - const auto vsync = nextAnticipatedVSyncTimeFromLocked(timestamp); + const auto vsync = snapToVsync(timestamp); if (!mLastVsyncSequence) return {vsync, 0}; const auto [slope, _] = getVSyncPredictionModelLocked(); @@ -257,7 +263,7 @@ auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSeq return {vsync, vsyncSequence}; } -nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const { +nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const { auto const [slope, intercept] = getVSyncPredictionModelLocked(); if (mTimestamps.empty()) { @@ -293,9 +299,29 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co return prediction; } -nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt) const { + ATRACE_CALL(); std::lock_guard lock(mMutex); + const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; + const auto threshold = currentPeriod / 2; + const auto minFramePeriod = minFramePeriodLocked().ns(); + const auto lastFrameMissed = + lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold; + const nsecs_t baseTime = + FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt + ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold) + : timePoint; + const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime); + if (FlagManager::getInstance().vrr_config()) { + const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); + const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); + mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); + } + return vsyncTime; +} +nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const { // update the mLastVsyncSequence for reference point mLastVsyncSequence = getVsyncSequenceLocked(timePoint); @@ -319,30 +345,12 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { }(); if (renderRatePhase == 0) { - const auto vsyncTime = mLastVsyncSequence->vsyncTime; - if (FlagManager::getInstance().vrr_config()) { - const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); - ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__, - ticks<std::milli, float>(vsyncTimePoint - TimePoint::now())); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); - } - return vsyncTime; + return mLastVsyncSequence->vsyncTime; } auto const [slope, intercept] = getVSyncPredictionModelLocked(); const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; - const auto nextAnticipatedVsyncTime = - nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); - if (FlagManager::getInstance().vrr_config()) { - const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime); - ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__, - ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now())); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr, - renderRate); - } - return nextAnticipatedVsyncTime; + return snapToVsync(approximateNextVsync - slope / 2); } /* @@ -397,7 +405,7 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { : std::nullopt; ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), to_string(*modePtr).c_str(), - timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A"); + timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); mDisplayModePtr = modePtr; @@ -445,6 +453,7 @@ void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, if (mLastVsyncSequence) { mLastVsyncSequence->vsyncTime += phase.ns(); } + mPastExpectedPresentTimes.clear(); } } } @@ -462,23 +471,17 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, lastConfirmedPresentTime.ns()) / 1e6f); } - mPastExpectedPresentTimes.push_back(expectedPresentTime); - const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; + mPastExpectedPresentTimes.push_back(expectedPresentTime); - const auto minFramePeriod = minFramePeriodLocked().ns(); while (!mPastExpectedPresentTimes.empty()) { const auto front = mPastExpectedPresentTimes.front().ns(); - const bool frontIsLastConfirmed = - std::abs(front - lastConfirmedPresentTime.ns()) < threshold; - const bool frontIsBeforeConfirmed = - front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold; - if (frontIsLastConfirmed || frontIsBeforeConfirmed) { + const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold; + if (frontIsBeforeConfirmed) { if (CC_UNLIKELY(mTraceOn)) { ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence", - static_cast<float>(lastConfirmedPresentTime.ns() - - mPastExpectedPresentTimes.front().ns()) / + static_cast<float>(lastConfirmedPresentTime.ns() - front) / 1e6f); } mPastExpectedPresentTimes.pop_front(); @@ -502,6 +505,7 @@ void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod); ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); + mLastMissedVsync = expectedPresentTime; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { @@ -515,6 +519,8 @@ VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { } void VSyncPredictor::clearTimestamps() { + ATRACE_CALL(); + if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); if (mKnownTimestamp) { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 72a343112a..919100363f 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -45,7 +45,9 @@ public: ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex); + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt = {}) const final + EXCLUDES(mMutex); nsecs_t currentPeriod() const final EXCLUDES(mMutex); Period minFramePeriod() const final EXCLUDES(mMutex); void resetModel() final EXCLUDES(mMutex); @@ -87,7 +89,8 @@ private: size_t next(size_t i) const REQUIRES(mMutex); bool validate(nsecs_t timestamp) const REQUIRES(mMutex); Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); - nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); + nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex); + nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex); bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); Period minFramePeriodLocked() const REQUIRES(mMutex); void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex); @@ -120,6 +123,8 @@ private: mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex); + + TimePoint mLastMissedVsync GUARDED_BY(mMutex); }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 24737e4fb2..186a2d6740 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -53,6 +53,8 @@ VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock, VSyncReactor::~VSyncReactor() = default; bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { + ATRACE_CALL(); + if (!fence) { return false; } @@ -64,6 +66,8 @@ bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { std::lock_guard lock(mMutex); if (mExternalIgnoreFences || mInternalIgnoreFences) { + ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d", + mExternalIgnoreFences, mInternalIgnoreFences); return true; } diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 1ed863cf7d..417163f4e1 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -57,9 +57,13 @@ public: * is updated. * * \param [in] timePoint The point in time after which to estimate a vsync event. + * \param [in] lastVsyncOpt The last vsync time used by the client. If provided, the tracker + * should use that as a reference point when generating the new vsync + * and avoid crossing the minimal frame period of a VRR display. * \return A prediction of the timestamp of a vsync event. */ - virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0; + virtual nsecs_t nextAnticipatedVSyncTimeFrom( + nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0; /* * The current period of the vsync signal. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index ff1c9e909a..db6a1871a7 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -19,6 +19,7 @@ #include <common/FlagManager.h> #include <ftl/fake_guard.h> +#include <gui/TraceUtils.h> #include <scheduler/Fps.h> #include <scheduler/Timer.h> @@ -179,6 +180,7 @@ void VsyncSchedule::enableHardwareVsync() { } void VsyncSchedule::enableHardwareVsyncLocked() { + ATRACE_CALL(); if (mHwVsyncState == HwVsyncState::Disabled) { getTracker().resetModel(); mRequestHardwareVsync(mId, true); @@ -187,6 +189,7 @@ void VsyncSchedule::enableHardwareVsyncLocked() { } void VsyncSchedule::disableHardwareVsync(bool disallow) { + ATRACE_CALL(); std::lock_guard<std::mutex> lock(mHwVsyncLock); switch (mHwVsyncState) { case HwVsyncState::Enabled: diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 9b417c6c96..b3692059e2 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -59,6 +59,7 @@ #include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> +#include <ftl/match.h> #include <ftl/unit.h> #include <gui/AidlStatusUtil.h> #include <gui/BufferQueue.h> @@ -318,6 +319,53 @@ bool fileNewerThan(const std::string& path, std::chrono::minutes duration) { return duration > (Clock::now() - updateTime); } +bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval, Period timeout, Duration threshold) { + if (lastFrameInterval.getPeriodNsecs() == 0) { + return false; + } + + const auto expectedPresentTimeDeltaNs = + expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); + + if (expectedPresentTimeDeltaNs > timeout.ns()) { + return false; + } + + const auto expectedPresentPeriods = static_cast<nsecs_t>( + std::round(static_cast<float>(expectedPresentTimeDeltaNs) / + static_cast<float>(lastFrameInterval.getPeriodNsecs()))); + const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; + const auto calculatedExpectedPresentTimeNs = + lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; + const auto presentTimeDelta = + std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); + return presentTimeDelta < threshold.ns(); +} + +bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime, + TimePoint lastExpectedPresentTimestamp, + std::optional<Period> timeoutOpt, Duration threshold) { + if (!timeoutOpt) { + // Always within timeout if timeoutOpt is absent and don't send hint + // for the timeout + return true; + } + + if (timeoutOpt->ns() == 0) { + // Always outside timeout if timeoutOpt is 0 and always send + // the hint for the timeout. + return false; + } + + if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { + return true; + } + + // Check if within the threshold as it can be just outside the timeout + return std::abs(expectedPresentTime.ns() - + (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); +} } // namespace anonymous // --------------------------------------------------------------------------- @@ -504,6 +552,12 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true); mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled || base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false); + + // These are set by the HWC implementation to indicate that they will use the workarounds. + mIsHotplugErrViaNegVsync = + base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); + + mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -716,7 +770,7 @@ void SurfaceFlinger::bootFinished() { sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { @@ -1182,20 +1236,21 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return NO_ERROR; } -void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) { - const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); +void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) { + const auto mode = desiredMode.mode; + const auto displayId = mode.modePtr->getPhysicalDisplayId(); + ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto display = getDisplayDeviceLocked(displayId); if (!display) { - ALOGW("%s: display is no longer valid", __func__); + ALOGW("%s: Unknown display %s", __func__, to_string(displayId).c_str()); return; } - const auto mode = request.mode; - const bool emitEvent = request.emitEvent; + const bool emitEvent = desiredMode.emitEvent; - switch (display->setDesiredMode(std::move(request), force)) { + switch (display->setDesiredMode(std::move(desiredMode), force)) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1245,7 +1300,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto displayOpt = FTL_FAKE_GUARD(mStateLock, ftl::find_if(mPhysicalDisplays, @@ -1291,61 +1346,55 @@ void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) { const auto displayId = display.getPhysicalId(); ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); - const auto pendingModeOpt = display.getPendingMode(); - if (!pendingModeOpt) { - // There is no pending mode change. This can happen if the active - // display changed and the mode change happened on a different display. - return; - } - - const auto& activeMode = pendingModeOpt->mode; - - if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) { - auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken()); - // We need to generate new sequenceId in order to recreate the display (and this - // way the framebuffer). - state.sequenceId = DisplayDeviceState{}.sequenceId; - state.physical->activeMode = activeMode.modePtr.get(); - processDisplayChangesLocked(); - - // processDisplayChangesLocked will update all necessary components so we're done here. - return; - } + ftl::match( + display.finalizeModeChange(), + [this, displayId](DisplayDevice::RefreshRateChange change) { + ftl::FakeGuard guard(mStateLock); - display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(), - activeMode.fps); - - if (displayId == mActiveDisplayId) { - mRefreshRateStats->setRefreshRate(activeMode.fps); - updatePhaseConfiguration(activeMode.fps); - } + if (change.activeMode.emitEvent) { + dispatchDisplayModeChangeEvent(displayId, change.activeMode.mode); + } - if (pendingModeOpt->emitEvent) { - dispatchDisplayModeChangeEvent(displayId, activeMode); - } + applyActiveMode(std::move(change.activeMode)); + }, + [&](DisplayDevice::ResolutionChange change) { + auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken()); + // Assign a new sequence ID to recreate the display and so its framebuffer. + state.sequenceId = DisplayDeviceState{}.sequenceId; + state.physical->activeMode = change.activeMode.mode.modePtr.get(); + + ftl::FakeGuard guard1(kMainThreadContext); + ftl::FakeGuard guard2(mStateLock); + processDisplayChangesLocked(); + + applyActiveMode(std::move(change.activeMode)); + }, + [](DisplayDevice::NoModeChange noChange) { + // TODO(b/255635821): Remove this case, as it should no longer happen. + ALOGE("A mode change was initiated but not finalized: %s", noChange.reason); + }); } -void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) { - display->clearDesiredMode(); - if (display->getPhysicalId() == mActiveDisplayId) { +void SurfaceFlinger::dropModeRequest(display::DisplayModeRequest&& request) { + if (request.mode.modePtr->getPhysicalDisplayId() == mActiveDisplayId) { // TODO(b/255635711): Check for pending mode changes on other displays. mScheduler->setModeChangePending(false); } } -void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { - const auto activeModeOpt = display->getDesiredMode(); - auto activeModePtr = activeModeOpt->mode.modePtr; +void SurfaceFlinger::applyActiveMode(display::DisplayModeRequest&& activeMode) { + auto activeModePtr = activeMode.mode.modePtr; const auto displayId = activeModePtr->getPhysicalDisplayId(); - const auto renderFps = activeModeOpt->mode.fps; + const auto renderFps = activeMode.mode.fps; - dropModeRequest(display); + dropModeRequest(std::move(activeMode)); constexpr bool kAllowToEnable = true; mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take()); mScheduler->setRenderRate(displayId, renderFps); if (displayId == mActiveDisplayId) { + mRefreshRateStats->setRefreshRate(renderFps); updatePhaseConfiguration(renderFps); } } @@ -1359,50 +1408,50 @@ void SurfaceFlinger::initiateDisplayModeChanges() { const auto display = getDisplayDeviceLocked(id); if (!display) continue; - auto desiredModeOpt = display->getDesiredMode(); + auto desiredModeOpt = display->takeDesiredMode(); if (!desiredModeOpt) { continue; } + auto desiredMode = std::move(*desiredModeOpt); + if (!shouldApplyRefreshRateSelectorPolicy(*display)) { - dropModeRequest(display); + dropModeRequest(std::move(desiredMode)); continue; } - const auto desiredModeId = desiredModeOpt->mode.modePtr->getId(); + const auto desiredModeId = desiredMode.mode.modePtr->getId(); const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId); if (!displayModePtrOpt) { - ALOGW("Desired display mode is no longer supported. Mode ID = %d", - desiredModeId.value()); - dropModeRequest(display); + ALOGW("%s: Unknown mode %d for display %s", __func__, desiredModeId.value(), + to_string(id).c_str()); + dropModeRequest(std::move(desiredMode)); continue; } - ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(), - to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), - to_string(display->getId()).c_str()); - - if (display->getActiveMode() == desiredModeOpt->mode) { - applyActiveMode(display); + if (display->getActiveMode() == desiredMode.mode) { + dropModeRequest(std::move(desiredMode)); continue; } - // Desired active mode was set, it is different than the mode currently in use, however - // allowed modes might have changed by the time we process the refresh. - // Make sure the desired mode is still allowed - if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) { - dropModeRequest(display); + // The desired mode is different from the active mode. However, the allowed modes might have + // changed since setDesiredMode scheduled a mode transition. + if (!display->refreshRateSelector().isModeAllowed(desiredMode.mode)) { + dropModeRequest(std::move(desiredMode)); continue; } - // TODO(b/142753666) use constrains + ALOGV("Mode setting display %s to %d (%s)", to_string(id).c_str(), desiredModeId.value(), + to_string(displayModePtrOpt.value().get()->getVsyncRate()).c_str()); + + // TODO(b/142753666): Use constraints. hal::VsyncPeriodChangeConstraints constraints; constraints.desiredTimeNanos = systemTime(); constraints.seamlessRequired = false; hal::VsyncPeriodChangeTimeline outTimeline; - if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) { + if (!display->initiateModeChange(std::move(desiredMode), constraints, outTimeline)) { continue; } @@ -1422,17 +1471,12 @@ void SurfaceFlinger::initiateDisplayModeChanges() { if (displayToUpdateImmediately) { const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately); finalizeDisplayModeChange(*display); - - const auto desiredModeOpt = display->getDesiredMode(); - if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) { - applyActiveMode(display); - } } } void SurfaceFlinger::disableExpensiveRendering() { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { ATRACE_NAME(whence); if (mPowerAdvisor->isUsingExpensiveRendering()) { for (const auto& [_, display] : mDisplays) { @@ -1474,7 +1518,7 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui: } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1555,7 +1599,7 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken, DisplayModeId modeId) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto snapshotOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1583,7 +1627,7 @@ status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& dis status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().clearBootDisplayMode(*displayId); } else { @@ -1622,7 +1666,7 @@ status_t SurfaceFlinger::setHdrConversionStrategy( ALOGE("hdrOutputConversion is not supported by this device."); return INVALID_OPERATION; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; @@ -1682,7 +1726,7 @@ status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const { void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { getHwComposer().setAutoLowLatencyMode(*displayId, on); } else { @@ -1693,7 +1737,7 @@ void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE; getHwComposer().setContentType(*displayId, type); @@ -1747,7 +1791,7 @@ status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& dis bool enable, uint8_t componentMask, uint64_t maxFrames) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, componentMask, maxFrames); @@ -1800,7 +1844,7 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) { outLayers->clear(); - auto future = mScheduler->schedule([=] { + auto future = mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); mDrawingState.traverseInZOrder([&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); @@ -1906,7 +1950,7 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, } const char* const whence = __func__; - return ftl::Future(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto display = getDisplayDeviceLocked(displayToken)) { const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported( @@ -2083,15 +2127,28 @@ nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - if (FlagManager::getInstance().connected_display()) { + if (FlagManager::getInstance().connected_display() && timestamp < 0 && + vsyncPeriod.has_value()) { // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32 - if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() && - vsyncPeriod.value() == ~0) { - int hotplugErrorCode = static_cast<int32_t>(-timestamp); - ALOGD("SurfaceFlinger got hotplugErrorCode=%d", hotplugErrorCode); + if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) { + const int32_t hotplugErrorCode = static_cast<int32_t>(-timestamp); + ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode, + hwcDisplayId); mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode); return; } + + if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { + const int32_t value = static_cast<int32_t>(-timestamp); + // one byte is good enough to encode android.hardware.drm.HdcpLevel + const int32_t maxLevel = (value >> 8) & 0xFF; + const int32_t connectedLevel = value & 0xFF; + ALOGD("SurfaceFlinger got HDCP level changed: connected=%d, max=%d for " + "display=%" PRIu64, + connectedLevel, maxLevel, hwcDisplayId); + updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); + return; + } } ATRACE_NAME(vsyncPeriod @@ -2160,19 +2217,16 @@ void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) { ATRACE_CALL(); if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) { - const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos); - ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue()); - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { - { - { - const auto display = getDisplayDeviceLocked(*displayId); - FTL_FAKE_GUARD(kMainThreadContext, - display->updateRefreshRateOverlayRate(fps, - display->getActiveMode() - .fps, - /* setByHwc */ true)); - } - } + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { + const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported() + ? data.refreshPeriodNanos + : data.vsyncPeriodNanos); + ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue()); + const auto display = getDisplayDeviceLocked(*displayId); + FTL_FAKE_GUARD(kMainThreadContext, + display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps, + /* setByHwc */ true)); })); } } @@ -2328,11 +2382,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, mLegacyLayers[layer->sequence] = layer; } } - if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) { - ATRACE_NAME("LayerHierarchyBuilder:update"); - mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(), - mLayerLifecycleManager.getDestroyedLayers()); - } + mLayerHierarchyBuilder.update(mLayerLifecycleManager); } bool mustComposite = false; @@ -2710,7 +2760,18 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); refreshArgs.expectedPresentTime = expectedPresentTime.ns(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; - + { + auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId]; + auto lastExpectedPresentTimestamp = TimePoint::fromNs( + notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns()); + if (expectedPresentTime > lastExpectedPresentTimestamp) { + // If the values are not same, then hint is sent with newer value. + // And because composition always follows the notifyExpectedPresentIfRequired, we can + // skip updating the lastExpectedPresentTimestamp in this case. + notifyExpectedPresentData.lastExpectedPresentTimestamp + .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime); + } + } // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); @@ -3377,9 +3438,10 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, } const sp<IBinder> token = sp<BBinder>::make(); + const ui::DisplayConnectionType connectionType = + getHwComposer().getDisplayConnectionType(displayId); - mPhysicalDisplays.try_emplace(displayId, token, displayId, - getHwComposer().getDisplayConnectionType(displayId), + mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType, std::move(displayModes), std::move(colorModes), std::move(info.deviceProductInfo)); @@ -3387,7 +3449,7 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, state.physical = {.id = displayId, .hwcDisplayId = hwcDisplayId, .activeMode = std::move(activeMode)}; - state.isSecure = true; // All physical displays are currently considered secure. + state.isSecure = connectionType == ui::DisplayConnectionType::Internal; state.isProtected = true; state.displayName = std::move(info.name); @@ -4064,24 +4126,64 @@ void SurfaceFlinger::onChoreographerAttached() { void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); - const auto timeout = [&]() -> std::optional<Period> { + const auto timeoutOpt = [&]() -> std::optional<Period> { const auto vrrConfig = modePtr->getVrrConfig(); if (!vrrConfig) return std::nullopt; const auto notifyExpectedPresentConfig = modePtr->getVrrConfig()->notifyExpectedPresentConfig; if (!notifyExpectedPresentConfig) return std::nullopt; - return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); + return Period::fromNs(notifyExpectedPresentConfig->timeoutNs); }(); - const auto displayId = modePtr->getPhysicalDisplayId(); - const auto status = getHwComposer().notifyExpectedPresentIfRequired(displayId, vsyncPeriod, - expectedPresentTime, - renderRate, timeout); - if (status != NO_ERROR) { - ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__, - displayId.value); + notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod, + expectedPresentTime, renderRate, timeoutOpt); +} + +void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, + Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) { + { + auto& data = mNotifyExpectedPresentMap[displayId]; + const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load(); + const auto lastFrameInterval = data.lastFrameInterval; + data.lastFrameInterval = frameInterval; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() + : kOneSecondNs); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, timeout, threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + + using fps_approx_ops::operator!=; + if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { + data.lastExpectedPresentTimestamp = expectedPresentTime; + } + + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return; + } + data.lastExpectedPresentTimestamp = expectedPresentTime; } + + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { + const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, + frameInterval); + if (status != NO_ERROR) { + ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, + displayId.value); + } + })); } void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { @@ -4153,9 +4255,6 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { sp<RegionSamplingThread>::make(*this, RegionSamplingThread::EnvironmentTimingTunables()); mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); - - mIsHotplugErrViaNegVsync = - base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); } void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) { @@ -4343,7 +4442,7 @@ status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinde if (mNumLayers >= MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), MAX_LAYERS); - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=, this] { ALOGE("Dumping layer keeping > 20 children alive:"); bool leakingParentLayerFound = false; mDrawingState.traverse([&](Layer* layer) { @@ -5927,7 +6026,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( kMainThreadContext) { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { @@ -6376,7 +6475,7 @@ void SurfaceFlinger::dumpOffscreenLayersProto(perfetto::protos::LayersProto& lay } perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { - return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get(); + return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get(); } void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { @@ -7296,8 +7395,8 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { if (!updateOverlay) return; // Update the overlay on the main thread to avoid race conditions with - // RefreshRateSelector::getActiveMode - static_cast<void>(mScheduler->schedule([=] { + // RefreshRateSelector::getActiveMode. + static_cast<void>(mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { ALOGW("%s: default display is null", __func__); @@ -7738,7 +7837,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } bool childrenOnly = args.childrenOnly; - RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { + RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> { ui::Transform layerTransform; Rect layerBufferSize; if (mLayerLifecycleManagerEnabled) { @@ -7877,7 +7976,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ATRACE_CALL(); auto future = mScheduler->schedule( - [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( + [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { ScreenCaptureResults captureResults; std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get(); @@ -8302,7 +8401,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return BAD_VALUE; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set desired display modes for invalid display token %p", @@ -8657,6 +8756,40 @@ status_t SurfaceFlinger::getStalledTransactionInfo( return NO_ERROR; } +void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, + int32_t maxLevel) { + if (!FlagManager::getInstance().connected_display()) { + return; + } + + Mutex::Autolock lock(mStateLock); + + const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId); + if (!idOpt) { + ALOGE("No display found for HDCP level changed event: connected=%d, max=%d for " + "display=%" PRIu64, + connectedLevel, maxLevel, hwcDisplayId); + return; + } + + const bool isInternalDisplay = + mPhysicalDisplays.get(*idOpt).transform(&PhysicalDisplay::isInternal).value_or(false); + if (isInternalDisplay) { + ALOGW("Unexpected HDCP level changed for internal display: connected=%d, max=%d for " + "display=%" PRIu64, + connectedLevel, maxLevel, hwcDisplayId); + return; + } + + static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() { + if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) { + Mutex::Autolock lock(mStateLock); + display->setSecure(connectedLevel >= 2 /* HDCP_V1 */); + } + mScheduler->onHdcpLevelsChanged(mAppConnectionHandle, displayId, connectedLevel, maxLevel); + })); +} + std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData( BufferData& bufferData, const char* layerName, uint64_t transactionId) { if (bufferData.buffer && diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6a7b3220b3..4440c7b746 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -654,6 +654,8 @@ private: status_t getStalledTransactionInfo( int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result); + void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel); + // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; @@ -725,9 +727,9 @@ private: void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext); void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext); - // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler. - void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock); - void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock); + // TODO(b/241285191): Move to Scheduler. + void dropModeRequest(display::DisplayModeRequest&&) REQUIRES(mStateLock); + void applyActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); // Called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) @@ -1276,6 +1278,7 @@ private: hal::Connection connection = hal::Connection::INVALID; }; + bool mIsHdcpViaNegVsync = false; bool mIsHotplugErrViaNegVsync = false; std::mutex mHotplugMutex; @@ -1469,7 +1472,7 @@ private: bool mLegacyFrontEndEnabled = true; frontend::LayerLifecycleManager mLayerLifecycleManager; - frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; + frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; frontend::LayerSnapshotBuilder mLayerSnapshotBuilder; std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles; @@ -1490,6 +1493,19 @@ private: // Map of displayid to mirrorRoot ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; + // NotifyExpectedPresentHint + struct NotifyExpectedPresentData { + // lastExpectedPresentTimestamp is read and write from multiple threads such as + // main thread, EventThread, MessageQueue. And is atomic for that reason. + std::atomic<TimePoint> lastExpectedPresentTimestamp{}; + Fps lastFrameInterval{}; + }; + std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap; + + void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt); + void sfdo_enableRefreshRateOverlay(bool active); void sfdo_setDebugFlash(int delay); void sfdo_scheduleComposite(); diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h index ddbf3e4873..6a66fff75f 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -218,6 +218,13 @@ class TransactionTraceWriter : public Singleton<TransactionTraceWriter> { friend class Singleton<TransactionTracing>; std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction = [](const std::string&, bool) {}; + std::atomic<bool> mEnabled{true}; + + void doInvoke(const std::string& filename, bool overwrite) { + if (mEnabled) { + mWriterFunction(filename, overwrite); + } + }; public: void setWriterFunction( @@ -225,12 +232,15 @@ public: mWriterFunction = std::move(function); } void invoke(const std::string& prefix, bool overwrite) { - mWriterFunction(TransactionTracing::getFilePath(prefix), overwrite); + doInvoke(TransactionTracing::getFilePath(prefix), overwrite); } /* pass in a complete file path for testing */ void invokeForTest(const std::string& filename, bool overwrite) { - mWriterFunction(filename, overwrite); + doInvoke(filename, overwrite); } + /* hacky way to avoid generating traces when converting transaction trace to layers trace. */ + void disable() { mEnabled.store(false); } + void enable() { mEnabled.store(true); } }; } // namespace android diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index c2d1954ee5..617ea2c566 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -40,9 +40,24 @@ namespace android { using namespace ftl::flag_operators; +namespace { +class ScopedTraceDisabler { +public: + ScopedTraceDisabler() { TransactionTraceWriter::getInstance().disable(); } + ~ScopedTraceDisabler() { TransactionTraceWriter::getInstance().enable(); } +}; +} // namespace + bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& traceFile, std::uint32_t traceFlags, LayerTracing& layerTracing, bool onlyLastEntry) { + // We are generating the layers trace by replaying back a set of transactions. If the + // transactions have unexpected states, we may generate a transaction trace to debug + // the unexpected state. This is silly. So we disable it by poking the + // TransactionTraceWriter. This is really a hack since we should manage our depenecies a + // little better. + ScopedTraceDisabler fatalErrorTraceDisabler; + if (traceFile.entry_size() == 0) { ALOGD("Trace file is empty"); return false; @@ -52,7 +67,7 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& // frontend frontend::LayerLifecycleManager lifecycleManager; - frontend::LayerHierarchyBuilder hierarchyBuilder{{}}; + frontend::LayerHierarchyBuilder hierarchyBuilder; frontend::LayerSnapshotBuilder snapshotBuilder; ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos; @@ -119,12 +134,10 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& lifecycleManager.applyTransactions(transactions, /*ignoreUnknownHandles=*/true); lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true); - if (lifecycleManager.getGlobalChanges().test( - frontend::RequestedLayerState::Changes::Hierarchy)) { - hierarchyBuilder.update(lifecycleManager.getLayers(), - lifecycleManager.getDestroyedLayers()); - } + // update hierarchy + hierarchyBuilder.update(lifecycleManager); + // update snapshots frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(), .layerLifecycleManager = lifecycleManager, .displays = displayInfos, diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 9b77751d1c..a27e10033d 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -122,12 +122,12 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(multithreaded_present); DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace); DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency); - DUMP_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved); + DUMP_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved); DUMP_READ_ONLY_FLAG(enable_fro_dependent_features); DUMP_READ_ONLY_FLAG(display_protected); DUMP_READ_ONLY_FLAG(fp16_client_target); DUMP_READ_ONLY_FLAG(game_default_frame_rate); - + DUMP_READ_ONLY_FLAG(enable_layer_command_batching); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -195,12 +195,13 @@ FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "") FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "") -FLAG_MANAGER_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved, +FLAG_MANAGER_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved, "debug.sf.cache_source_crop_only_moved") FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "") FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "") FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target") FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "") +FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "") /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 3a509f6352..2f2895c82e 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -61,11 +61,12 @@ public: bool multithreaded_present() const; bool add_sf_skipped_frames_to_trace() const; bool use_known_refresh_rate_for_fps_consistency() const; - bool cache_if_source_crop_layer_only_moved() const; + bool cache_when_source_crop_layer_only_moved() const; bool enable_fro_dependent_features() const; bool display_protected() const; bool fp16_client_target() const; bool game_default_frame_rate() const; + bool enable_layer_command_batching() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index b690d8d98e..649ad252d8 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -134,13 +134,13 @@ void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* disp dispatch->schedule(tmp, {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()}); }, "o.o"); dispatch->schedule(tmp, {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()}); dispatch->unregisterCallback(tmp); dispatch->cancel(tmp); } @@ -162,20 +162,20 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { entry.update(*stubTracker, 0); entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, + .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()}, *stubTracker, 0); entry.disarm(); entry.ensureNotRunning(); entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, + .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()}, *stubTracker, 0); auto const wakeup = entry.wakeupTime(); auto const ready = entry.readyTime(); entry.callback(entry.executing(), *wakeup, *ready); entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()}); dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); } diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index fa307e9bb4..114f3b0e7a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -86,7 +86,10 @@ public: bool addVsyncTimestamp(nsecs_t /* timestamp */) override { return true; } - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; } + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */, + std::optional<nsecs_t>) const override { + return 1; + } nsecs_t currentPeriod() const override { return 1; } Period minFramePeriod() const override { return Period::fromNs(currentPeriod()); } diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 3f26448a54..b65a2b3814 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -32,6 +32,14 @@ flag { } flag { + name: "enable_layer_command_batching" + namespace: "core_graphics" + description: "This flag controls batching on createLayer/destroyLayer command with executeCommand." + bug: "290685621" + is_fixed_read_only: true +} + +flag { name: "dont_skip_on_early" namespace: "core_graphics" description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" @@ -92,8 +100,18 @@ flag { bug: "299201319" } +# This flag is broken. +# See alternative one: cache_when_source_crop_layer_only_moved +# flag { +# name: "cache_if_source_crop_layer_only_moved" +# namespace: "core_graphics" +# description: "do not flatten layers if source crop is only moved" +# bug: "305718400" +# is_fixed_read_only: true +# } + flag { - name: "cache_if_source_crop_layer_only_moved" + name: "cache_when_source_crop_layer_only_moved" namespace: "core_graphics" description: "do not flatten layers if source crop is only moved" bug: "305718400" diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index dea019431b..c75f90222a 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -106,6 +106,7 @@ cc_test { "SurfaceFlinger_HdrOutputControlTest.cpp", "SurfaceFlinger_HotplugTest.cpp", "SurfaceFlinger_InitializeDisplaysTest.cpp", + "SurfaceFlinger_NotifyExpectedPresentTest.cpp", "SurfaceFlinger_NotifyPowerBoostTest.cpp", "SurfaceFlinger_PowerHintTest.cpp", "SurfaceFlinger_SetDisplayStateTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index c463a9271b..220001b3b0 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -23,10 +23,13 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \ - ASSERT_TRUE(requestOpt); \ - EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \ - EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent) +#define EXPECT_DISPLAY_MODE_REQUEST(expected, request) \ + EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, request.mode); \ + EXPECT_EQ(expected.emitEvent, request.emitEvent) + +#define EXPECT_DISPLAY_MODE_REQUEST_OPT(expected, requestOpt) \ + ASSERT_TRUE(requestOpt); \ + EXPECT_DISPLAY_MODE_REQUEST(expected, (*requestOpt)) namespace android { namespace { @@ -37,6 +40,7 @@ using DisplayModeRequest = display::DisplayModeRequest; class InitiateModeChangeTest : public DisplayTransactionTest { public: using Action = DisplayDevice::DesiredModeAction; + void SetUp() override { injectFakeBufferQueueFactory(); injectFakeNativeWindowSurfaceFactory(); @@ -84,36 +88,43 @@ TEST_F(InitiateModeChangeTest, setDesiredModeToActiveMode) { TEST_F(InitiateModeChangeTest, setDesiredMode) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); + EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode()); EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120))); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode()); + EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode()); } -TEST_F(InitiateModeChangeTest, clearDesiredMode) { +TEST_F(InitiateModeChangeTest, takeDesiredMode) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); - EXPECT_TRUE(mDisplay->getDesiredMode()); + EXPECT_EQ(kDesiredMode90, mDisplay->getDesiredMode()); - mDisplay->clearDesiredMode(); + EXPECT_EQ(kDesiredMode90, mDisplay->takeDesiredMode()); EXPECT_FALSE(mDisplay->getDesiredMode()); } -TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) { +TEST_F(InitiateModeChangeTest, initiateModeChange) FTL_FAKE_GUARD(kMainThreadContext) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); + EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode()); const hal::VsyncPeriodChangeConstraints constraints{ .desiredTimeNanos = systemTime(), .seamlessRequired = false, }; hal::VsyncPeriodChangeTimeline timeline; - EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); - mDisplay->clearDesiredMode(); + auto desiredModeOpt = mDisplay->takeDesiredMode(); + ASSERT_TRUE(desiredModeOpt); EXPECT_FALSE(mDisplay->getDesiredMode()); + + EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline)); + + auto modeChange = mDisplay->finalizeModeChange(); + + auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange); + ASSERT_TRUE(changePtr); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode); } TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) { @@ -125,27 +136,47 @@ TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) { TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); + EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode()); const hal::VsyncPeriodChangeConstraints constraints{ .desiredTimeNanos = systemTime(), .seamlessRequired = false, }; hal::VsyncPeriodChangeTimeline timeline; - EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); + auto desiredModeOpt = mDisplay->takeDesiredMode(); + ASSERT_TRUE(desiredModeOpt); + EXPECT_FALSE(mDisplay->getDesiredMode()); + + EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline)); + + auto modeChange = mDisplay->finalizeModeChange(); + auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange); + ASSERT_TRUE(changePtr); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode); + + // The latest request should override the desired mode. + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60))); EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120))); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode()); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); + EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode()); - EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode()); + // No pending mode change. + EXPECT_TRUE( + std::holds_alternative<DisplayDevice::NoModeChange>(mDisplay->finalizeModeChange())); - mDisplay->clearDesiredMode(); + desiredModeOpt = mDisplay->takeDesiredMode(); + ASSERT_TRUE(desiredModeOpt); EXPECT_FALSE(mDisplay->getDesiredMode()); + + EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline)); + + modeChange = mDisplay->finalizeModeChange(); + + changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange); + ASSERT_TRUE(changePtr); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, changePtr->activeMode); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index ee12276994..6671414ba6 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -481,14 +481,17 @@ constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1; -template <typename PhysicalDisplay, int width, int height> +template <typename PhysicalDisplay, int width, int height, + Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal) + ? Secure::TRUE + : Secure::FALSE> struct PhysicalDisplayVariant - : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, - Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, + : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure, + PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, - Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, + Async::FALSE, secure, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, PhysicalDisplay> {}; @@ -515,6 +518,7 @@ struct SecondaryDisplay { }; struct TertiaryDisplay { + static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External; static constexpr Primary PRIMARY = Primary::FALSE; static constexpr uint8_t PORT = 253; static constexpr HWDisplayId HWC_DISPLAY_ID = 1003; diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 8891c06c75..4e8a609be6 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -471,7 +471,7 @@ TEST_F(EventThreadTest, getLatestVsyncEventData) { mock::VSyncTracker& mockTracker = *static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker()); - EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _)) .WillOnce(Return(preferredExpectedPresentationTime)); VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection); diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index d3ce4f2b90..a5c0657868 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -21,6 +21,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <optional> #include <vector> // StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be @@ -83,14 +84,7 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } - void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, - Fps lastFrameInterval) { - ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end()); - auto& displayData = mHwc.mDisplayData.at(displayId); - std::scoped_lock lock{displayData.expectedPresentLock}; - displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; - displayData.lastFrameInterval = lastFrameInterval; - } + void setVrrTimeoutHint(bool status) { mHwc.mEnableVrrTimeout = status; } }; TEST_F(HWComposerTest, isHeadless) { @@ -143,7 +137,7 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); { EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) @@ -235,7 +229,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); { EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) @@ -324,7 +318,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true)); { EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _)) @@ -332,6 +326,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty()); } { + setVrrTimeoutHint(true); constexpr int32_t kWidth = 480; constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; @@ -339,10 +334,8 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { const hal::VrrConfig vrrConfig = hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(), .notifyExpectedPresentConfig = hal::VrrConfig:: - NotifyExpectedPresentConfig{.notifyExpectedPresentHeadsUpNs = - ms2ns(30), - .notifyExpectedPresentTimeoutNs = - ms2ns(30)}}; + NotifyExpectedPresentConfig{.headsUpNs = ms2ns(30), + .timeoutNs = ms2ns(30)}}; hal::DisplayConfiguration displayConfiguration{.configId = kConfigId, .width = kWidth, .height = kHeight, @@ -372,9 +365,9 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { displayConfiguration.dpi = {kDpi, kDpi}; EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _)) - .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{ - displayConfiguration}), - Return(HalError::NONE))); + .WillRepeatedly(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{ + displayConfiguration}), + Return(HalError::NONE))); modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); EXPECT_EQ(modes.size(), size_t{1}); @@ -386,6 +379,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_EQ(modes.front().vrrConfig, vrrConfig); EXPECT_EQ(modes.front().dpiX, kDpi); EXPECT_EQ(modes.front().dpiY, kDpi); + + setVrrTimeoutHint(false); + modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); + EXPECT_EQ(modes.front().vrrConfig->notifyExpectedPresentConfig, std::nullopt); } } @@ -417,144 +414,6 @@ TEST_F(HWComposerTest, onVsyncInvalid) { EXPECT_FALSE(displayIdOpt); } -TEST_F(HWComposerTest, notifyExpectedPresentTimeout) { - constexpr hal::HWDisplayId kHwcDisplayId = 2; - expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); - ASSERT_TRUE(info); - - auto expectedPresentTime = systemTime() + ms2ns(10); - static constexpr Fps Fps60Hz = 60_Hz; - static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs(); - static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); - static constexpr Period kVsyncPeriod = - Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); - static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); - static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); - - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz)); - - { - // Very first ExpectedPresent after idle, no previous timestamp - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // Absent timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - /*timeoutOpt*/ std::nullopt); - } - { - // Timeout is 0 - expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - Period::fromNs(0)); - } - { - // ExpectedPresent is after the timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent has not changed - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent is after the last reported ExpectedPresent. - expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, - // representing we changed our decision and want to present earlier than previously - // reported. - expectedPresentTime -= kFrameInterval120HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } -} - -TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { - constexpr hal::HWDisplayId kHwcDisplayId = 2; - expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); - ASSERT_TRUE(info); - - const auto now = systemTime(); - auto expectedPresentTime = now; - static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); - - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0))); - static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); - static constexpr Period kVsyncPeriod = - Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); - - struct FrameRateIntervalTestData { - int32_t frameIntervalNs; - bool callExpectedPresent; - }; - const std::vector<FrameRateIntervalTestData> frameIntervals = { - {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, - {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, - {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, - {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, - {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, - {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, - {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, - }; - - for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { - { - expectedPresentTime += frameIntervalNs; - if (callExpectedPresent) { - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, - frameIntervalNs)) - .WillOnce(Return(HalError::NONE)); - } else { - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - } - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), - Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); - } - } -} - struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent), (override)); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp index 95f19406b4..2b333f4b87 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -45,7 +45,8 @@ protected: // reparenting tests TEST_F(LayerHierarchyTest, addLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -64,7 +65,8 @@ TEST_F(LayerHierarchyTest, addLayer) { } TEST_F(LayerHierarchyTest, reparentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, 11); reparentLayer(111, 12); reparentLayer(1221, 1); @@ -79,7 +81,8 @@ TEST_F(LayerHierarchyTest, reparentLayer) { } TEST_F(LayerHierarchyTest, reparentLayerToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, UNASSIGNED_LAYER_ID); reparentLayer(11, UNASSIGNED_LAYER_ID); @@ -96,7 +99,8 @@ TEST_F(LayerHierarchyTest, reparentLayerToNull) { } TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, UNASSIGNED_LAYER_ID); reparentLayer(11, UNASSIGNED_LAYER_ID); reparentLayer(1221, UNASSIGNED_LAYER_ID); @@ -115,7 +119,8 @@ TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) { } TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); destroyLayerHandle(111); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -139,7 +144,8 @@ TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) { } TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); reparentLayer(11, 1); @@ -154,7 +160,8 @@ TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) { // offscreen tests TEST_F(LayerHierarchyTest, layerMovesOnscreen) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -170,7 +177,8 @@ TEST_F(LayerHierarchyTest, layerMovesOnscreen) { } TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -187,7 +195,8 @@ TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) { // rel-z tests TEST_F(LayerHierarchyTest, setRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -200,7 +209,8 @@ TEST_F(LayerHierarchyTest, setRelativeParent) { } TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -216,7 +226,8 @@ TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { } TEST_F(LayerHierarchyTest, reparentToRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -231,7 +242,8 @@ TEST_F(LayerHierarchyTest, reparentToRelativeParent) { } TEST_F(LayerHierarchyTest, setParentAsRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -246,7 +258,8 @@ TEST_F(LayerHierarchyTest, setParentAsRelativeParent) { } TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -262,7 +275,8 @@ TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { } TEST_F(LayerHierarchyTest, reparentRelativeLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -294,7 +308,8 @@ TEST_F(LayerHierarchyTest, reparentRelativeLayer) { // mirror tests TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -308,7 +323,8 @@ TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { } TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); @@ -324,7 +340,8 @@ TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) { TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) { mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); createLayer(1111, 111); createLayer(112, 11); @@ -340,7 +357,8 @@ TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) { // mirror & relatives tests TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(111, 12); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); @@ -371,7 +389,8 @@ TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) { } TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(1221, 12); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12); @@ -401,7 +420,8 @@ TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) { } TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); reparentLayer(2, UNASSIGNED_LAYER_ID); @@ -427,7 +447,8 @@ TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { } TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(1221, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -462,7 +483,8 @@ TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) { } TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); updateBackgroundColor(1, 0.5); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -485,7 +507,8 @@ TEST_F(LayerHierarchyTest, ParentBecomesTheChild) { createLayer(11, 1); reparentLayer(1, 11); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); std::vector<uint32_t> expectedTraversalPath = {}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -502,17 +525,11 @@ TEST_F(LayerHierarchyTest, RelativeLoops) { createLayer(11, 1); reparentRelativeLayer(11, 2); reparentRelativeLayer(2, 11); - mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); - - // fix loop - uint32_t invalidRelativeRoot; - bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); - EXPECT_TRUE(hasRelZLoop); - mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); - hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); - EXPECT_EQ(invalidRelativeRoot, 11u); - EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + LayerHierarchyBuilder hierarchyBuilder; + // this call is expected to fix the loop! + hierarchyBuilder.update(mLifecycleManager); + uint32_t unused; + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused)); std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -534,16 +551,11 @@ TEST_F(LayerHierarchyTest, IndirectRelativeLoops) { createLayer(221, 22); reparentRelativeLayer(22, 111); reparentRelativeLayer(11, 221); - mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); - - // fix loop - uint32_t invalidRelativeRoot; - bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); - EXPECT_TRUE(hasRelZLoop); - mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); - hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); - EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + LayerHierarchyBuilder hierarchyBuilder; + // this call is expected to fix the loop! + hierarchyBuilder.update(mLifecycleManager); + uint32_t unused; + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused)); std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -554,7 +566,8 @@ TEST_F(LayerHierarchyTest, IndirectRelativeLoops) { } TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(1, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -568,7 +581,8 @@ TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) { TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) { // remove default hierarchy mLifecycleManager = LayerLifecycleManager(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); createRootLayer(1); destroyLayerHandle(1); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -582,7 +596,8 @@ TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) { // traversal path test TEST_F(LayerHierarchyTest, traversalPathId) { setZ(122, -1); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); auto checkTraversalPathIdVisitor = [](const LayerHierarchy& hierarchy, const LayerHierarchy::TraversalPath& traversalPath) -> bool { @@ -605,7 +620,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerSequenceId) { createLayer(53, 5); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -639,7 +655,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerZ) { setZ(13, 1); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -661,7 +678,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) { setLayerStack(2, 10); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -672,7 +690,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) { } TEST_F(LayerHierarchyTest, canMirrorDisplay) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -687,7 +706,8 @@ TEST_F(LayerHierarchyTest, canMirrorDisplay) { } TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5)); setLayerStack(3, 1); @@ -701,7 +721,8 @@ TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) { } TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -719,7 +740,8 @@ TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) { } TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -737,7 +759,8 @@ TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) { } TEST_F(LayerHierarchyTest, canMirrorDisplayWithMirrors) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(12, UNASSIGNED_LAYER_ID); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); UPDATE_AND_VERIFY(hierarchyBuilder); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 7e9abce690..67e624922c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -176,14 +176,12 @@ protected: void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); } void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { - if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - hierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); - } + hierarchyBuilder.update(mLifecycleManager); mLifecycleManager.commitChanges(); // rebuild layer hierarchy from scratch and verify that it matches the updated state. - LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder newBuilder; + newBuilder.update(mLifecycleManager); EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), getTraversalPath(newBuilder.getHierarchy())); EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), @@ -512,10 +510,7 @@ protected: } void update(LayerSnapshotBuilder& snapshotBuilder) { - if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - mHierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); - } + mHierarchyBuilder.update(mLifecycleManager); LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), .layerLifecycleManager = mLifecycleManager, .includeMetadata = false, @@ -530,7 +525,7 @@ protected: mLifecycleManager.commitChanges(); } - LayerHierarchyBuilder mHierarchyBuilder{{}}; + LayerHierarchyBuilder mHierarchyBuilder; DisplayInfos mFrontEndDisplayInfos; bool mHasDisplayChanges = false; diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 787fa1c5e4..e9d231956f 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -56,7 +56,7 @@ protected: LayerHistoryIntegrationTest() : LayerSnapshotTestBase() { mFlinger.resetScheduler(mScheduler); mLifecycleManager = {}; - mHierarchyBuilder = {{}}; + mHierarchyBuilder = {}; } void updateLayerSnapshotsAndLayerHistory(nsecs_t now) { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 50cd784725..ba32c68b61 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -58,8 +58,7 @@ protected: void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) { if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - mHierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); + mHierarchyBuilder.update(mLifecycleManager); } args.root = mHierarchyBuilder.getHierarchy(); actualBuilder.update(args); diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 9aa089f900..e9c4d801a9 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -91,7 +91,7 @@ namespace { TEST_F(MessageQueueTest, commit) { const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); @@ -105,7 +105,7 @@ TEST_F(MessageQueueTest, commitTwice) { InSequence s; const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); @@ -124,7 +124,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { InSequence s; const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); @@ -151,7 +151,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { const auto timingAfterCallback = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = kPresentTime.ns()}; + .lastVsync = kPresentTime.ns()}; EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); @@ -163,7 +163,7 @@ TEST_F(MessageQueueTest, commitWithDurationChange) { const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index e515895a1a..7fdca716fb 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -98,7 +98,7 @@ protected: mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback}; - surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; + surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; @@ -584,17 +584,17 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); vrrTracker->addVsyncTimestamp(0); - // Next frame at refresh rate as no previous frame - EXPECT_EQ(refreshRate, - scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(0))); - EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(500))); + TimePoint::fromNs(1000))); EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(1500))); + TimePoint::fromNs(2000))); + // Not crossing the min frame period + EXPECT_EQ(Fps::fromPeriodNsecs(1500), + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), + TimePoint::fromNs(2500))); // Change render rate frameRate = Fps::fromPeriodNsecs(2000); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); @@ -602,10 +602,10 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(2500))); + TimePoint::fromNs(2000))); EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(4500))); + TimePoint::fromNs(4000))); } TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 7ad97a2133..3291dc366a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -53,7 +53,7 @@ public: auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return( TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); @@ -138,14 +138,14 @@ void DisplayModeSwitchingTest::setupScheduler( auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly( Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, minFramePeriod()) .WillRepeatedly(Return(Period::fromNs( TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), std::move(selectorPtr), @@ -179,7 +179,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ Mock::VerifyAndClearExpectations(mComposer); - EXPECT_TRUE(mDisplay->getDesiredMode()); + EXPECT_FALSE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that the next commit will complete the mode change and send @@ -263,11 +263,13 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120); + // The 120 Hz mode should be pending. + EXPECT_FALSE(mDisplay->getDesiredMode()); + EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90); mFlinger.commit(); + // The 120 Hz mode should be active. EXPECT_FALSE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120); } @@ -324,7 +326,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K); } -MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { +MATCHER_P2(HasDesiredMode, flinger, modeId, "") { if (!arg->getDesiredMode()) { *result_listener << "No desired mode"; return false; @@ -343,12 +345,33 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { return true; } -MATCHER_P(ModeSettledTo, modeId, "") { +MATCHER_P(HasPendingMode, modeId, "") { + const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg); + + if (!pendingOpt) { + *result_listener << "No pending mode"; + return false; + } + + if (pendingOpt->mode.modePtr->getId() != modeId) { + *result_listener << "Unexpected pending mode " << modeId; + return false; + } + + return true; +} + +MATCHER_P(HasActiveMode, modeId, "") { if (const auto desiredOpt = arg->getDesiredMode()) { *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId(); return false; } + if (const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg)) { + *result_listener << "Unsettled pending mode " << pendingOpt->mode.modePtr->getId(); + return false; + } + ftl::FakeGuard guard(kMainThreadContext); if (arg->getActiveMode().modePtr->getId() != modeId) { @@ -365,14 +388,14 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_TRUE(innerDisplay->isPoweredOn()); EXPECT_FALSE(outerDisplay->isPoweredOn()); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); // Only the inner display is powered on. mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), @@ -384,8 +407,8 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { mock::createDisplayModeSpecs(kModeId60.value(), false, 0.f, 120.f))); - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_CALL(*mComposer, @@ -395,13 +418,13 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); innerDisplay->setPowerMode(hal::PowerMode::OFF); outerDisplay->setPowerMode(hal::PowerMode::ON); @@ -409,8 +432,8 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { // Only the outer display is powered on. mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60)); EXPECT_CALL(*mComposer, setActiveConfigWithConstraints(kOuterDisplayHwcId, @@ -419,13 +442,13 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60)); mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60)); } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { @@ -434,16 +457,16 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { EXPECT_TRUE(innerDisplay->isPoweredOn()); EXPECT_FALSE(outerDisplay->isPoweredOn()); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); outerDisplay->setPowerMode(hal::PowerMode::ON); // Both displays are powered on. mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), @@ -455,8 +478,8 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { mock::createDisplayModeSpecs(kModeId60.value(), false, 0.f, 120.f))); - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_CALL(*mComposer, @@ -471,25 +494,25 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60)); mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60)); } TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { EXPECT_TRUE(mDisplay->isPoweredOn()); - EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(mDisplay, HasActiveMode(kModeId60)); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), mock::createDisplayModeSpecs(kModeId90.value(), false, 0.f, 120.f))); - EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); + EXPECT_THAT(mDisplay, HasDesiredMode(&mFlinger, kModeId90)); // Power off the display before the mode has been set. mDisplay->setPowerMode(hal::PowerMode::OFF); @@ -504,11 +527,11 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { // Powering off should not abort the mode set. EXPECT_FALSE(mDisplay->isPoweredOn()); - EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); + EXPECT_THAT(mDisplay, HasPendingMode(kModeId90)); mFlinger.commit(); - EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90)); + EXPECT_THAT(mDisplay, HasActiveMode(kModeId90)); } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { @@ -517,16 +540,16 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_TRUE(innerDisplay->isPoweredOn()); EXPECT_FALSE(outerDisplay->isPoweredOn()); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); outerDisplay->setPowerMode(hal::PowerMode::ON); // Both displays are powered on. mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), @@ -538,8 +561,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { mock::createDisplayModeSpecs(kModeId60.value(), false, 0.f, 120.f))); - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60)); // Power off the outer display before the mode has been set. outerDisplay->setPowerMode(hal::PowerMode::OFF); @@ -553,13 +576,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { mFlinger.commit(); // Powering off the inactive display should abort the mode set. - EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120)); innerDisplay->setPowerMode(hal::PowerMode::OFF); outerDisplay->setPowerMode(hal::PowerMode::ON); @@ -575,13 +598,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { mFlinger.commit(); // The mode set should resume once the display becomes active. - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60)); mFlinger.commit(); - EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); - EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90)); + EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60)); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp index fc5f2b0b90..1583f64c0a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp @@ -46,7 +46,7 @@ TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) { EXPECT_CALL(static_cast<mock::VSyncTracker&>( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), - nextAnticipatedVSyncTimeFrom(_)) + nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(0)); // -------------------------------------------------------------------- diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp new file mode 100644 index 0000000000..7206e2977d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2023 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. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "DisplayTransactionTestHelpers.h" + +namespace android { + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +class NotifyExpectedPresentTest : public DisplayTransactionTest { +public: + void SetUp() override { + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); + FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary) + .setPowerMode(hal::PowerMode::ON) + .inject(&mFlinger, mComposer); + } + +protected: + sp<DisplayDevice> mDisplay; + static constexpr bool kIsPrimary = true; + static constexpr hal::HWDisplayId HWC_DISPLAY_ID = + FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; +}; + +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { + const auto physicDisplayId = mDisplay->getPhysicalId(); + auto expectedPresentTime = systemTime() + ms2ns(10); + static constexpr Fps kFps60Hz = 60_Hz; + static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); + static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); + static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); + + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, + kLastExpectedPresentTimestamp, + kFps60Hz)); + + { + // Very first ExpectedPresent after idle, no previous timestamp + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // Absent timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + /*timeoutOpt*/ std::nullopt); + } + { + // Timeout is 0 + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + Period::fromNs(0)); + } + { + // ExpectedPresent is after the timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent has not changed + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent is after the last reported ExpectedPresent. + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, + // representing we changed our decision and want to present earlier than previously + // reported. + expectedPresentTime -= kFrameInterval120HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } +} + +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { + const auto physicDisplayId = mDisplay->getPhysicalId(); + const auto now = systemTime(); + auto expectedPresentTime = now; + static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); + + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, + TimePoint::fromNs(now), + Fps::fromValue(0))); + static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + + struct FrameRateIntervalTestData { + int32_t frameIntervalNs; + bool callExpectedPresent; + }; + const std::vector<FrameRateIntervalTestData> frameIntervals = { + {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, + {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, + }; + + for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { + { + expectedPresentTime += frameIntervalNs; + if (callExpectedPresent) { + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + frameIntervalNs)) + .WillOnce(Return(Error::NONE)); + } else { + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + } + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), + kTimeoutNs); + } + } +} +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 8ba6bf87e2..16815be1b3 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -303,13 +303,13 @@ public: auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock); auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, minFramePeriod()) .WillRepeatedly( Return(Period::fromNs(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, @@ -484,6 +484,10 @@ public: return mFlinger->setDisplayBrightness(display, brightness); } + static const auto& getPendingMode(const sp<DisplayDevice>& display) { + return display->mPendingModeOpt; + } + // Allow reading display state without locking, as if called on the SF main thread. auto setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { @@ -695,6 +699,21 @@ public: mFlinger->mLegacyFrontEndEnabled = false; } + void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt) { + mFlinger->notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, + frameInterval, timeoutOpt); + } + + void setNotifyExpectedPresentData(PhysicalDisplayId displayId, + TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval) { + auto& displayData = mFlinger->mNotifyExpectedPresentMap[displayId]; + displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; + displayData.lastFrameInterval = lastFrameInterval; + } + ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may diff --git a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp index 4a83d445fc..d071ce985f 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp @@ -105,4 +105,16 @@ TEST_F(TransactionTraceWriterTest, overwriteOldFile) { verifyTraceFile(); } +// Check we cannot write to file if the trace write is disabled. +TEST_F(TransactionTraceWriterTest, canDisableTraceWriter) { + TransactionTraceWriter::getInstance().disable(); + TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true); + EXPECT_NE(access(mFilename.c_str(), F_OK), 0); + + TransactionTraceWriter::getInstance().enable(); + TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true); + EXPECT_EQ(access(mFilename.c_str(), F_OK), 0); + verifyTraceFile(); +} + } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 6a5635305a..d891008683 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -64,7 +64,7 @@ class FixedRateIdealStubTracker : public StubTracker { public: FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {} - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final { + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final { auto const floor = timePoint % mPeriod; if (floor == 0) { return timePoint; @@ -77,7 +77,7 @@ class VRRStubTracker : public StubTracker { public: VRRStubTracker(nsecs_t period) : StubTracker(period) {} - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final { + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final { std::lock_guard lock(mMutex); auto const normalized_to_base = time_point - mBase; auto const floor = (normalized_to_base) % mPeriod; @@ -117,7 +117,7 @@ public: mCallback.schedule( {.workDuration = mWorkload, .readyDuration = mReadyDuration, - .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration}); + .lastVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration}); for (auto i = 0u; i < iterations - 1; i++) { std::unique_lock lock(mMutex); @@ -130,7 +130,7 @@ public: mCallback.schedule({.workDuration = mWorkload, .readyDuration = mReadyDuration, - .earliestVsync = last + mWorkload + mReadyDuration}); + .lastVsync = last + mWorkload + mReadyDuration}); } // wait for the last callback. diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 2047018a15..4bf58de05c 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -46,14 +46,14 @@ using namespace com::android::graphics::surfaceflinger; class MockVSyncTracker : public mock::VSyncTracker { public: MockVSyncTracker(nsecs_t period) : mPeriod{period} { - ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) + ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_, _)) .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime)); ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); ON_CALL(*this, currentPeriod()) .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod)); } - nsecs_t nextVSyncTime(nsecs_t timePoint) const { + nsecs_t nextVSyncTime(nsecs_t timePoint, std::optional<nsecs_t>) const { if (timePoint % mPeriod == 0) { return timePoint; } @@ -243,10 +243,9 @@ TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { mDispatchGroupThreshold, mVsyncMoveThreshold); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 1000}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -257,10 +256,9 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); @@ -277,16 +275,14 @@ TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq); CountingCallback cb(mDispatch); - auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); result = - mDispatch->update(cb, - {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(700, *result); @@ -303,17 +299,18 @@ TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) { CountingCallback cb(mDispatch); const auto result = - mDispatch->update(cb, - {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended}); EXPECT_FALSE(result.has_value()); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(mPeriod))) + .WillOnce(Return(1150)); EXPECT_CALL(mMockClock, alarmAt(_, 1050)); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -324,7 +321,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { auto const now = 234; mMockClock.advanceBy(234); auto const workDuration = 10 * mPeriod; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(now + workDuration, std::optional<nsecs_t>(mPeriod))) .WillOnce(Return(mPeriod * 11)); EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); @@ -332,7 +330,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { const auto result = mDispatch->schedule(cb, {.workDuration = workDuration, .readyDuration = 0, - .earliestVsync = mPeriod}); + .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod, *result); } @@ -342,10 +340,9 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled); @@ -356,10 +353,9 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); mMockClock.advanceBy(950); @@ -371,10 +367,9 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { EXPECT_CALL(mMockClock, alarmCancel()); PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); @@ -393,10 +388,9 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { PausingCallback cb(mDispatch, 50ms); cb.stashResource(resource); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); @@ -413,7 +407,8 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { } TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .Times(4) .WillOnce(Return(1055)) .WillOnce(Return(1063)) @@ -428,8 +423,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); - mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod}); advanceToNextCallback(); advanceToNextCallback(); @@ -441,7 +436,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { } TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(4) .WillOnce(Return(1000)) .WillOnce(Return(2000)) @@ -455,21 +450,21 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 0}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); EXPECT_THAT(cb.mCalls[0], Eq(1000)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(2)); EXPECT_THAT(cb.mCalls[1], Eq(2000)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); advanceToNextCallback(); @@ -478,7 +473,7 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { } TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(4) .WillOnce(Return(10000)) .WillOnce(Return(1000)) @@ -493,9 +488,8 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); - mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod * 10}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod}); mDispatch->cancel(cb1); } @@ -507,9 +501,9 @@ TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -522,9 +516,9 @@ TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -542,10 +536,9 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, - {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = closeOffset, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb0.mCalls.size(), Eq(1)); @@ -553,11 +546,9 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { ASSERT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 2000}); mDispatch->schedule(cb1, - {.workDuration = notCloseOffset, - .readyDuration = 0, - .earliestVsync = 2000}); + {.workDuration = notCloseOffset, .readyDuration = 0, .lastVsync = 2000}); advanceToNextCallback(); ASSERT_THAT(cb1.mCalls.size(), Eq(2)); EXPECT_THAT(cb1.mCalls[1], Eq(2000)); @@ -577,32 +568,32 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled); } TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(3) .WillOnce(Return(950)) .WillOnce(Return(1975)) .WillOnce(Return(2950)); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 920}); mMockClock.advanceBy(850); EXPECT_THAT(cb.mCalls.size(), Eq(1)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1900}); mMockClock.advanceBy(900); EXPECT_THAT(cb.mCalls.size(), Eq(1)); mMockClock.advanceBy(125); EXPECT_THAT(cb.mCalls.size(), Eq(2)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2900}); mMockClock.advanceBy(975); EXPECT_THAT(cb.mCalls.size(), Eq(3)); } @@ -616,13 +607,11 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { tmp = mDispatch->registerCallback( [&](auto, auto, auto) { mDispatch->schedule(tmp, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 2000}); + {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); }, "o.o"); - mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -631,30 +620,29 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { std::optional<nsecs_t> lastTarget; tmp = mDispatch->registerCallback( [&](auto timestamp, auto, auto) { - auto result = - mDispatch->schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp - mVsyncMoveThreshold}); + auto result = mDispatch->schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .lastVsync = timestamp - mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod + timestamp - 400, *result); result = mDispatch->schedule(tmp, {.workDuration = 400, .readyDuration = 0, - .earliestVsync = timestamp}); + .lastVsync = timestamp}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod + timestamp - 400, *result); result = mDispatch->schedule(tmp, {.workDuration = 400, .readyDuration = 0, - .earliestVsync = timestamp + mVsyncMoveThreshold}); + .lastVsync = timestamp + mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod + timestamp - 400, *result); lastTarget = timestamp; }, "oo"); - mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); EXPECT_THAT(lastTarget, Eq(1000)); @@ -670,16 +658,16 @@ TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .lastVsync = 1000}); mMockClock.advanceBy(750); - mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); - mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 2000}); mMockClock.advanceBy(800); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); } TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { @@ -692,12 +680,12 @@ TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); - mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); - mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .lastVsync = 2000}); + mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); advanceToNextCallback(); @@ -709,8 +697,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 20000}); } TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { @@ -720,17 +708,15 @@ TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); CountingCallback cb0(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); mDispatch->cancel(cb0); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); } TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { VSyncDispatch::CallbackToken token(100); EXPECT_FALSE( - mDispatch - ->schedule(token, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) + mDispatch->schedule(token, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}) .has_value()); EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error)); } @@ -738,12 +724,10 @@ TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); - result = mDispatch->schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -755,14 +739,12 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1200, *result); @@ -779,14 +761,12 @@ TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTarge EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(400, *result); @@ -795,19 +775,18 @@ TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTarge } TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1002)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(602, *result); } @@ -815,13 +794,12 @@ TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedul TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); advanceToNextCallback(); - result = mDispatch->schedule(cb0, - {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); + result = + mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -832,13 +810,12 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); advanceToNextCallback(); - result = mDispatch->schedule(cb0, - {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); + result = + mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1100, *result); } @@ -850,13 +827,11 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); @@ -872,13 +847,11 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(0, *result); @@ -892,10 +865,10 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMove) { VSyncCallbackRegistration cb( mDispatch, [](auto, auto, auto) {}, ""); VSyncCallbackRegistration cb1(std::move(cb)); - cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); cb.cancel(); - cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); cb1.cancel(); } @@ -908,10 +881,10 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { VSyncCallbackRegistration cb1( mDispatch, [](auto, auto, auto) {}, ""); cb1 = std::move(cb); - cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); cb.cancel(); - cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); cb1.cancel(); } @@ -924,16 +897,14 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); mMockClock.advanceBy(80); @@ -952,16 +923,14 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch->schedule(cb, - {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1630, *result); mMockClock.advanceBy(80); @@ -978,12 +947,10 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); @@ -1007,12 +974,10 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); @@ -1034,21 +999,21 @@ TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { CountingCallback cb2(mDispatch); Sequence seq; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(1000)); EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(1000)); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(610, *result); @@ -1070,10 +1035,9 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 70, - .readyDuration = 30, - .earliestVsync = intended}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 70, .readyDuration = 30, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); advanceToNextCallback(); @@ -1094,8 +1058,8 @@ TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); @@ -1118,8 +1082,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); @@ -1139,14 +1103,12 @@ TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(300); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1200, *result); @@ -1162,14 +1124,12 @@ TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(300); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(300, *result); @@ -1204,7 +1164,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); @@ -1219,14 +1179,15 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) auto const duration = 500; auto const now = 8750; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(now + duration, std::optional<nsecs_t>(994))) .Times(1) .WillOnce(Return(10000)); VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, + EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994}, *mStubTracker.get(), now) .has_value()); auto const wakeup = entry.wakeupTime(); @@ -1249,7 +1210,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { }, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); @@ -1272,7 +1233,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { } TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1020)); @@ -1284,7 +1245,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { entry.update(*mStubTracker.get(), 0); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); auto wakeup = entry.wakeupTime(); @@ -1300,7 +1261,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); entry.update(*mStubTracker.get(), 0); @@ -1313,24 +1274,24 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); entry.executing(); // 1000 is executing // had 1000 not been executing, this could have been scheduled for time 800. - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001}, *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); @@ -1343,23 +1304,25 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); Sequence seq; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500))) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500))) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold, + std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); entry.executing(); // 1000 is executing - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); } @@ -1367,16 +1330,16 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); } @@ -1386,9 +1349,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); - entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400}); + entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .lastVsync = 400}); entry.addPendingWorkloadUpdate( - {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400}); + {.workDuration = effectualOffset, .readyDuration = 0, .lastVsync = 400}); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); entry.update(*mStubTracker.get(), 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); @@ -1410,7 +1373,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { }, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, + EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500}, *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 7a498c9d29..961ba578ca 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -662,7 +662,7 @@ TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); NotifyExpectedPresentConfig notifyExpectedPresentConfig; - notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = Period::fromNs(30).ns(); + notifyExpectedPresentConfig.timeoutNs = Period::fromNs(30).ns(); hal::VrrConfig vrrConfig; vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; @@ -720,15 +720,17 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { vrrTracker.setRenderRate(minFrameRate); vrrTracker.addVsyncTimestamp(0); EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); - EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); - EXPECT_EQ(1500, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); - EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); - - vrrTracker.onFrameMissed(TimePoint::fromNs(2500)); - EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); - EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3300)); + EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000)); + EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500)); + + // Miss when starting 4500 and expect the next vsync will be at 5000 (next one) + vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500)); + vrrTracker.onFrameMissed(TimePoint::fromNs(4500)); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); } } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index d649679ac8..184dada32e 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -51,7 +51,7 @@ public: ~Composer() override; MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override)); - MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override)); + MOCK_METHOD(bool, isVrrSupported, (), (const, override)); MOCK_METHOD0(getCapabilities, std::vector<aidl::android::hardware::graphics::composer3::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h index a088aabc11..ed1405b058 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h @@ -18,12 +18,21 @@ #include "binder/Status.h" +// FMQ library in IPower does questionable conversions +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> +#pragma clang diagnostic pop + #include <gmock/gmock.h> using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::ChannelConfig; using aidl::android::hardware::power::IPower; using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::SessionConfig; +using aidl::android::hardware::power::SessionTag; + using aidl::android::hardware::power::Mode; using android::binder::Status; @@ -42,6 +51,14 @@ public: int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, SessionTag tag, SessionConfig* config, + std::shared_ptr<IPowerHintSession>* _aidl_return), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, + (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); + MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 364618d61a..27564b26de 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -18,10 +18,15 @@ #include "binder/Status.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> +#pragma clang diagnostic pop + #include <gmock/gmock.h> using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::SessionConfig; using aidl::android::hardware::power::SessionHint; using aidl::android::hardware::power::SessionMode; using android::binder::Status; @@ -47,6 +52,7 @@ public: MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override)); MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override)); MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h index 68fe3c52d4..b17c8adf6c 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h @@ -19,7 +19,10 @@ #include <gmock/gmock.h> #include <scheduler/Time.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <powermanager/PowerHalController.h> +#pragma clang diagnostic pop namespace android { namespace hardware { diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 866af3bbd0..e2b0ed1df9 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -59,6 +59,9 @@ public: MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&)); MOCK_METHOD(void, pauseVsyncCallback, (bool)); MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override)); + MOCK_METHOD(void, onHdcpLevelsChanged, + (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), + (override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index e588bb9a3f..3870983133 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -28,7 +28,8 @@ public: ~VSyncTracker() override; MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override)); - MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t), (const, override)); + MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>), + (const, override)); MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override)); MOCK_METHOD(Period, minFramePeriod, (), (const, override)); MOCK_METHOD(void, resetModel, (), (override)); diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index 7c8e695d67..159b2d5305 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -63,7 +63,8 @@ extern "C" { /* * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11 * - * This version of the extension deprecates the last of grallocusage + * This version of the extension deprecates the last of grallocusage and + * extends VkNativeBufferANDROID to support passing AHardwareBuffer* */ #define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" @@ -111,6 +112,9 @@ typedef struct { * usage: gralloc usage requested when the buffer was allocated * usage2: gralloc usage requested when the buffer was allocated * usage3: gralloc usage requested when the buffer was allocated + * ahb: The AHardwareBuffer* from the actual ANativeWindowBuffer. Caller + * maintains ownership of resource. AHardwareBuffer pointer is only valid + * for the duration of the function call */ typedef struct { VkStructureType sType; @@ -121,6 +125,7 @@ typedef struct { int usage; /* DEPRECATED in SPEC_VERSION 6 */ VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */ uint64_t usage3; /* ADDED in SPEC_VERSION 9 */ + struct AHardwareBuffer* ahb; /* ADDED in SPEC_VERSION 11 */ } VkNativeBufferANDROID; /* diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 4f7b0f4c57..0df5e77181 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1970,6 +1970,8 @@ VkResult CreateSwapchainKHR(VkDevice device, &image_native_buffer.usage2.producer, &image_native_buffer.usage2.consumer); image_native_buffer.usage3 = img.buffer->usage; + image_native_buffer.ahb = + ANativeWindowBuffer_getHardwareBuffer(img.buffer.get()); image_create.pNext = &image_native_buffer; ATRACE_BEGIN("CreateImage"); @@ -2146,7 +2148,12 @@ VkResult AcquireNextImageKHR(VkDevice device, .stride = buffer->stride, .format = buffer->format, .usage = int(buffer->usage), + .usage3 = buffer->usage, + .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer), }; + android_convertGralloc0To1Usage(int(buffer->usage), + &nb.usage2.producer, + &nb.usage2.consumer); VkBindImageMemoryInfo bimi = { .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, .pNext = &nb, @@ -2692,7 +2699,12 @@ static void InterceptBindImageMemory2( .stride = buffer->stride, .format = buffer->format, .usage = int(buffer->usage), + .usage3 = buffer->usage, + .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer), }; + android_convertGralloc0To1Usage(int(buffer->usage), + &native_buffer.usage2.producer, + &native_buffer.usage2.consumer); // Reserve enough space to avoid letting re-allocation invalidate the // addresses of the elements inside. out_native_buffers->reserve(bind_info_count); diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index a6d540bb93..5112e14dc8 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -46,5 +46,8 @@ cc_library_shared { "hwvulkan_headers", "vulkan_headers", ], - shared_libs: ["liblog"], + shared_libs: [ + "liblog", + "libnativewindow", + ], } diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 2e87f1718b..973e71c892 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android/hardware_buffer.h> #include <hardware/hwvulkan.h> #include <errno.h> diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index 935535f5bf..d34851e536 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -16,6 +16,8 @@ // WARNING: This file is generated. See ../README.md for instructions. +#include <android/hardware_buffer.h> + #include <algorithm> #include "null_driver_gen.h" |