diff options
150 files changed, 3400 insertions, 1059 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 1b61e3ab74..6576ffdc54 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2195,6 +2195,74 @@ static void DumpstateOnboardingOnly() { ds.AddDir(LOGPERSIST_DATA_DIR, false); } +static std::string GetTimestamp(const timespec& ts) { + tm tm; + localtime_r(&ts.tv_sec, &tm); + + // Reserve enough space for the entire time string, includes the space + // for the '\0' to make the calculations below easier by using size for + // the total string size. + std::string str(sizeof("1970-01-01 00:00:00.123456789+0830"), '\0'); + size_t n = strftime(str.data(), str.size(), "%F %H:%M", &tm); + if (n == 0) { + return "TIMESTAMP FAILURE"; + } + int num_chars = snprintf(&str[n], str.size() - n, ":%02d.%09ld", tm.tm_sec, ts.tv_nsec); + if (num_chars > str.size() - n) { + return "TIMESTAMP FAILURE"; + } + n += static_cast<size_t>(num_chars); + if (strftime(&str[n], str.size() - n, "%z", &tm) == 0) { + return "TIMESTAMP FAILURE"; + } + return str; +} + +static std::string GetCmdline(pid_t pid) { + std::string cmdline; + if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), + &cmdline)) { + return "UNKNOWN"; + } + // There are '\0' terminators between arguments, convert them to spaces. + // But start by skipping all trailing '\0' values. + size_t cur = cmdline.size() - 1; + while (cur != 0 && cmdline[cur] == '\0') { + cur--; + } + if (cur == 0) { + return "UNKNOWN"; + } + while ((cur = cmdline.rfind('\0', cur)) != std::string::npos) { + cmdline[cur] = ' '; + } + return cmdline; +} + +static void DumpPidHeader(int fd, pid_t pid, const timespec& ts) { + // For consistency, the header to this message matches the one + // dumped by debuggerd. + dprintf(fd, "\n----- pid %d at %s -----\n", pid, GetTimestamp(ts).c_str()); + dprintf(fd, "Cmd line: %s\n", GetCmdline(pid).c_str()); +} + +static void DumpPidFooter(int fd, pid_t pid) { + // For consistency, the footer to this message matches the one + // dumped by debuggerd. + dprintf(fd, "----- end %d -----\n", pid); +} + +static bool DumpBacktrace(int fd, pid_t pid, bool is_java_process) { + int ret = dump_backtrace_to_file_timeout( + pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd); + if (ret == -1 && is_java_process) { + // Tried to unwind as a java process, try a native unwind. + dprintf(fd, "Java unwind failed for pid %d, trying a native unwind.\n", pid); + ret = dump_backtrace_to_file_timeout(pid, kDebuggerdNativeBacktrace, 3, fd); + } + return ret != -1; +} + Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX"; const size_t buf_size = temp_file_pattern.length() + 1; @@ -2243,16 +2311,6 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { continue; } - // Skip cached processes. - if (IsCached(pid)) { - // For consistency, the header and footer to this message match those - // dumped by debuggerd in the success case. - dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid); - dprintf(fd, "Dump skipped for cached process.\n"); - dprintf(fd, "---- end %d ----", pid); - continue; - } - const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); std::string exe; if (!android::base::Readlink(link_name, &exe)) { @@ -2281,16 +2339,31 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { break; } + timespec start_timespec; + clock_gettime(CLOCK_REALTIME, &start_timespec); + if (IsCached(pid)) { + DumpPidHeader(fd, pid, start_timespec); + dprintf(fd, "Process is cached, skipping backtrace due to high chance of timeout.\n"); + DumpPidFooter(fd, pid); + continue; + } + const uint64_t start = Nanotime(); - const int ret = dump_backtrace_to_file_timeout( - pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd); + if (!DumpBacktrace(fd, pid, is_java_process)) { + if (IsCached(pid)) { + DumpPidHeader(fd, pid, start_timespec); + dprintf(fd, "Backtrace failed, but process has become cached.\n"); + DumpPidFooter(fd, pid); + continue; + } - if (ret == -1) { - // For consistency, the header and footer to this message match those - // dumped by debuggerd in the success case. - dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid); - dprintf(fd, "Dump failed, likely due to a timeout.\n"); - dprintf(fd, "---- end %d ----", pid); + DumpPidHeader(fd, pid, start_timespec); + dprintf(fd, "Backtrace gathering failed, likely due to a timeout.\n"); + DumpPidFooter(fd, pid); + + dprintf(fd, "\n[dump %s stack %d: %.3fs elapsed]\n", + is_java_process ? "dalvik" : "native", pid, + (float)(Nanotime() - start) / NANOS_PER_SEC); timeout_failures++; continue; } diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h index 88801ca671..0ec5abbd15 100644 --- a/cmds/installd/file_parsing.h +++ b/cmds/installd/file_parsing.h @@ -51,7 +51,7 @@ bool ParseFile(std::istream& input_stream, Func parse) { } template<typename Func> -bool ParseFile(std::string_view str_file, Func parse) { +bool ParseFile(const std::string& str_file, Func parse) { std::ifstream ifs(str_file); if (!ifs.is_open()) { return false; diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index ae7d8e04d4..2d889fd80d 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -50,7 +50,27 @@ else exit 1 fi -if pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then +# A source that infinitely emits arbitrary lines. +# When connected to STDIN of another process, this source keeps STDIN open until +# the consumer process closes STDIN or this script dies. +function infinite_source { + while echo .; do + sleep 1 + done +} + +# Delegate to Pre-reboot Dexopt, a feature of ART Service. +# ART Service decides what to do with this request: +# - If Pre-reboot Dexopt is disabled or unsupported, the command returns +# non-zero. This is always the case if the current system is Android 14 or +# earlier. +# - If Pre-reboot Dexopt is enabled in synchronous mode, the command blocks +# until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds or +# not. This is the default behavior if the current system is Android 15. +# - If Pre-reboot Dexopt is enabled in asynchronous mode, the command schedules +# an asynchronous job and returns 0 immediately. The job will then run by the +# job scheduler when the device is idle and charging. +if infinite_source | pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then # Handled by Pre-reboot Dexopt. exit 0 fi diff --git a/include/android/input.h b/include/android/input.h index fec56f02f9..ee98d7aee9 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -1002,7 +1002,6 @@ enum { * Keyboard types. * * Refer to the documentation on android.view.InputDevice for more details. - * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated. */ enum { /** none */ diff --git a/include/input/Input.h b/include/input/Input.h index 3ca9c19876..ec08cdd163 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -99,6 +99,18 @@ enum { /* Motion event is inconsistent with previously sent motion events. */ AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED, + + /** Private flag, not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = + android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION, + + /** Private flag, not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants:: + MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + + /** Mask for all private flags that are not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, }; /** @@ -209,8 +221,12 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) * Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or * pointing upwards in the negative Y direction, a positive angle points towards the right, and a * negative angle points towards the left. + * + * If the angle represents a direction that needs to be preserved, set isDirectional to true to get + * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set + * isDirectional to false to get an output range of [-pi/2, pi/2]. */ -float transformAngle(const ui::Transform& transform, float angleRadians); +float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional); /** * The type of the InputEvent. @@ -258,6 +274,16 @@ enum class KeyState { ftl_last = VIRTUAL, }; +/** + * The keyboard type. This should have 1:1 correspondence with the values of anonymous enum + * defined in input.h + */ +enum class KeyboardType { + NONE = AINPUT_KEYBOARD_TYPE_NONE, + NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, + ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC, +}; + bool isStylusToolType(ToolType toolType); struct PointerProperties; @@ -462,8 +488,6 @@ struct PointerCoords { // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); - void transform(const ui::Transform& transform); - inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); } @@ -930,10 +954,12 @@ public: // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods // are used to apply these transformations for different axes. static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); - static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&, - const PointerCoords&); - static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&, - const PointerCoords&); + static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags, + const ui::Transform&, const PointerCoords&); + static void calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source, + int32_t flags, const ui::Transform&); + static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags, + const ui::Transform&, const PointerCoords&); // The rounding precision for transformed motion events. static constexpr float ROUNDING_PRECISION = 0.001f; diff --git a/include/input/KeyboardClassifier.h b/include/input/KeyboardClassifier.h new file mode 100644 index 0000000000..457d474ee7 --- /dev/null +++ b/include/input/KeyboardClassifier.h @@ -0,0 +1,53 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/result.h> +#include <input/Input.h> +#include <input/InputDevice.h> + +#include "rust/cxx.h" + +namespace android { + +namespace input { +namespace keyboardClassifier { +struct KeyboardClassifier; +} +} // namespace input + +/* + * Keyboard classifier to classify keyboard into alphabetic and non-alphabetic keyboards + */ +class KeyboardClassifier { +public: + KeyboardClassifier(); + /** + * Get the type of keyboard that the classifier currently believes the device to be. + */ + KeyboardType getKeyboardType(DeviceId deviceId); + void notifyKeyboardChanged(DeviceId deviceId, const InputDeviceIdentifier& identifier, + uint32_t deviceClasses); + void processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState); + +private: + std::optional<rust::Box<android::input::keyboardClassifier::KeyboardClassifier>> + mRustClassifier; + std::unordered_map<DeviceId, KeyboardType> mKeyboardTypeMap; +}; + +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 090e35bc37..cb1d1143bc 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -28,6 +28,7 @@ cc_library_headers { recovery_available: true, host_supported: true, native_bridge_supported: true, + cmake_snapshot_supported: true, header_libs: [ "libbinder_headers_platform_shared", @@ -83,6 +84,124 @@ cc_library_headers { }, } +cc_cmake_snapshot { + name: "binder_sdk", + modules: [ + "libbinder_sdk", + "libbinder_sdk_single_threaded", + "libbinder_ndk_sdk", + "binderRpcTestNoKernel", + ], + prebuilts: [ + // to enable arm64 host support, build with musl - e.g. on aosp_cf_arm64_phone + "aidl", + "libc++", + ], + include_sources: true, + cflags: [ + "-DNDEBUG", + "-DBINDER_ENABLE_LIBLOG_ASSERT", + "-DBINDER_DISABLE_NATIVE_HANDLE", + "-DBINDER_DISABLE_BLOB", + "-DBINDER_NO_LIBBASE", + "-DBINDER_NO_KERNEL_IPC_TESTING", + + // from Soong's global.go commonGlobalCflags and noOverrideGlobalCflags + "-Wno-c99-designator", + "-Wno-missing-field-initializers", + + // warnings that only pop up on gcc + "-Wno-unknown-pragmas", // "pragma clang" + "-Wno-attributes", // attributes on compound-statements + "-Wno-psabi", // reminders about old ABI changes + ], + cflags_ignored: [ + // gcc requires all header constexprs to be used in all dependent compilatinon units + "-Wunused-const-variable", + ], + library_mapping: [ + { + android_name: "libssl", + mapped_name: "ssl", + package_pregenerated: "external/boringssl", + }, + { + android_name: "libcrypto", + mapped_name: "crypto", + package_pregenerated: "external/boringssl", + }, + { + android_name: "libgtest", + mapped_name: "GTest::gtest", + package_system: "GTest", + }, + { + android_name: "libgtest_main", + mapped_name: "GTest::gtest", + package_system: "GTest", + }, + + // use libbinder_sdk and friends instead of full Android's libbinder + { + android_name: "libbinder_rpc_no_kernel", + mapped_name: "android::libbinder_sdk", + }, + { + android_name: "libbinder_rpc_single_threaded_no_kernel", + mapped_name: "android::libbinder_sdk_single_threaded", + }, + { + android_name: "libbinder_headers", + mapped_name: "android::libbinder_headers_base", + }, + { + android_name: "libbinder", + mapped_name: "android::libbinder_sdk", + }, + { + android_name: "libbinder_ndk", + mapped_name: "android::libbinder_ndk_sdk", + }, + { + android_name: "liblog", + mapped_name: "android::liblog_stub", + }, + + // explicitly included by Binder tests, but not needed outside of Android + { + android_name: "libbase", + }, + { + android_name: "libcutils", + }, + { + android_name: "libutils", + }, + + // disable tests that don't work outside of Android yet + { + android_name: "binder_rpc_test_service", + }, + { + android_name: "binder_rpc_test_service_single_threaded", + }, + + // trusty mocks are artificially triggered and not needed outside of Android build + { + android_name: "libbinder_on_trusty_mock", + }, + { + android_name: "libbinder_ndk_on_trusty_mock", + }, + { + android_name: "binderRpcTestService_on_trusty_mock", + }, + { + android_name: "binderRpcTest_on_trusty_mock", + }, + ], +} + // These interfaces are android-specific implementation unrelated to binder // transport itself and should be moved to AIDL or in domain-specific libs. // @@ -126,6 +245,9 @@ cc_defaults { header_libs: [ "libbinder_headers_base", ], + export_header_lib_headers: [ + "libbinder_headers_base", + ], cflags: [ "-Wextra", @@ -369,6 +491,7 @@ cc_library { double_loadable: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, + cmake_snapshot_supported: false, // libbinder does not offer a stable wire protocol. // if a second copy of it is installed, then it may break after security @@ -408,16 +531,8 @@ cc_library { afdo: true, } -cc_library_host_shared { - name: "libbinder_sdk", - - defaults: [ - "libbinder_common_defaults", - ], - - shared_libs: [ - "libutils_binder_sdk", - ], +cc_defaults { + name: "binder_sdk_defaults", cflags: [ "-DBINDER_ENABLE_LIBLOG_ASSERT", @@ -429,6 +544,21 @@ cc_library_host_shared { header_libs: [ "liblog_stub", ], +} + +cc_defaults { + name: "libbinder_sdk_defaults", + + cmake_snapshot_supported: true, + + defaults: [ + "libbinder_common_defaults", + "binder_sdk_defaults", + ], + + shared_libs: [ + "libutils_binder_sdk", + ], srcs: [ "OS_non_android_linux.cpp", @@ -446,6 +576,19 @@ cc_library_host_shared { }, } +cc_library_host_shared { + name: "libbinder_sdk", + defaults: ["libbinder_sdk_defaults"], +} + +cc_library_host_shared { + name: "libbinder_sdk_single_threaded", + defaults: ["libbinder_sdk_defaults"], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], +} + cc_library { name: "libbinder_rpc_no_kernel", vendor_available: true, @@ -535,6 +678,7 @@ cc_defaults { defaults: ["libbinder_tls_shared_deps"], vendor_available: true, host_supported: true, + cmake_snapshot_supported: true, header_libs: [ "libbinder_headers", diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 7a855e1c5b..6594aa6309 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "BpBinder" -#define ATRACE_TAG ATRACE_TAG_AIDL //#define LOG_NDEBUG 0 #include <binder/BpBinder.h> @@ -24,15 +23,10 @@ #include <binder/IResultReceiver.h> #include <binder/RpcSession.h> #include <binder/Stability.h> +#include <binder/Trace.h> #include <stdio.h> -#ifndef __TRUSTY__ -#include <cutils/trace.h> -#else -#define ATRACE_INT(...) -#endif - #include "BuildFlags.h" #include "file.h" @@ -216,7 +210,7 @@ sp<BpBinder> BpBinder::create(int32_t handle) { sTrackingMap[trackedUid]++; } uint32_t numProxies = sBinderProxyCount.fetch_add(1, std::memory_order_relaxed); - ATRACE_INT("binder_proxies", numProxies); + binder::os::trace_int(ATRACE_TAG_AIDL, "binder_proxies", numProxies); uint32_t numLastWarned = sBinderProxyCountWarned.load(std::memory_order_relaxed); uint32_t numNextWarn = numLastWarned + kBinderProxyCountWarnInterval; if (numProxies >= numNextWarn) { @@ -640,8 +634,8 @@ BpBinder::~BpBinder() { } } } - [[maybe_unused]] uint32_t numProxies = --sBinderProxyCount; - ATRACE_INT("binder_proxies", numProxies); + uint32_t numProxies = --sBinderProxyCount; + binder::os::trace_int(ATRACE_TAG_AIDL, "binder_proxies", numProxies); if (ipc) { ipc->expungeHandle(binderHandle(), this); ipc->decWeakHandle(binderHandle()); diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index ef96f803c3..fbc812571a 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1027,6 +1027,7 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) goto finish; case BR_FROZEN_REPLY: + ALOGW("Transaction failed because process frozen."); err = FAILED_TRANSACTION; goto finish; @@ -1578,8 +1579,8 @@ void IPCThreadState::logExtendedError() { } #endif - ALOGE_IF(ee.command != BR_OK, "Binder transaction failure: %d/%d/%d", - ee.id, ee.command, ee.param); + ALOGE_IF(ee.command != BR_OK, "Binder transaction failure. id: %d, BR_*: %d, error: %d (%s)", + ee.id, ee.command, ee.param, strerror(-ee.param)); } void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/, diff --git a/libs/binder/OS.h b/libs/binder/OS.h index 5703eb7515..04869a170f 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -26,6 +26,7 @@ namespace android::binder::os { LIBBINDER_EXPORTED void trace_begin(uint64_t tag, const char* name); LIBBINDER_EXPORTED void trace_end(uint64_t tag); +LIBBINDER_EXPORTED void trace_int(uint64_t tag, const char* name, int32_t value); status_t setNonBlocking(borrowed_fd fd); diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp index 1eace857fb..893ee15578 100644 --- a/libs/binder/OS_android.cpp +++ b/libs/binder/OS_android.cpp @@ -44,6 +44,10 @@ void trace_end(uint64_t tag) { atrace_end(tag); } +void trace_int(uint64_t tag, const char* name, int32_t value) { + atrace_int(tag, name, value); +} + } // namespace os // Legacy trace symbol. To be removed once all of downstream rebuilds. diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp index 01f3fe0329..0c64eb61c6 100644 --- a/libs/binder/OS_non_android_linux.cpp +++ b/libs/binder/OS_non_android_linux.cpp @@ -39,6 +39,8 @@ void trace_begin(uint64_t, const char*) {} void trace_end(uint64_t) {} +void trace_int(uint64_t, const char*, int32_t) {} + uint64_t GetThreadId() { return syscall(__NR_gettid); } diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index 15047152a1..5b157cc7c3 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -82,13 +82,12 @@ namespace os { } \ } -#define RETURN_IF_ENTRY_ERASED(map, key) \ - { \ - size_t num_erased = (map).erase(key); \ - if (num_erased) { \ - ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ - return num_erased; \ - } \ +#define RETURN_IF_ENTRY_ERASED(map, key) \ + { \ + size_t num_erased = (map).erase(key); \ + if (num_erased) { \ + return num_erased; \ + } \ } status_t PersistableBundle::writeToParcel(Parcel* parcel) const { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index fb2781be59..8485ecd835 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -516,22 +516,23 @@ String8 ProcessState::getDriverName() { return mDriverName; } -static unique_fd open_driver(const char* driver) { +static unique_fd open_driver(const char* driver, String8* error) { auto fd = unique_fd(open(driver, O_RDWR | O_CLOEXEC)); if (!fd.ok()) { - PLOGE("Opening '%s' failed", driver); + error->appendFormat("%d (%s) Opening '%s' failed", errno, strerror(errno), driver); return {}; } int vers = 0; int result = ioctl(fd.get(), BINDER_VERSION, &vers); if (result == -1) { - PLOGE("Binder ioctl to obtain version failed"); + error->appendFormat("%d (%s) Binder ioctl to obtain version failed", errno, + strerror(errno)); return {}; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! " - "ioctl() return value: %d", - vers, BINDER_CURRENT_PROTOCOL_VERSION, result); + error->appendFormat("Binder driver protocol(%d) does not match user space protocol(%d)! " + "ioctl() return value: %d", + vers, BINDER_CURRENT_PROTOCOL_VERSION, result); return {}; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; @@ -565,7 +566,8 @@ ProcessState::ProcessState(const char* driver) mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { - unique_fd opened = open_driver(driver); + String8 error; + unique_fd opened = open_driver(driver, &error); if (opened.ok()) { // mmap the binder, providing a chunk of virtual address space to receive transactions. @@ -580,8 +582,9 @@ ProcessState::ProcessState(const char* driver) } #ifdef __ANDROID__ - LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.", - driver); + LOG_ALWAYS_FATAL_IF(!opened.ok(), + "Binder driver '%s' could not be opened. Error: %s. Terminating.", + error.c_str(), driver); #endif if (opened.ok()) { diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index e8d9829b5d..40102bb9f0 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -129,9 +129,9 @@ public: setupUnixDomainSocketBootstrapClient(binder::unique_fd bootstrap); /** - * Connects to an RPC server at the CVD & port. + * Connects to an RPC server at the CID & port. */ - [[nodiscard]] LIBBINDER_EXPORTED status_t setupVsockClient(unsigned int cvd, unsigned int port); + [[nodiscard]] LIBBINDER_EXPORTED status_t setupVsockClient(unsigned int cid, unsigned int port); /** * Connects to an RPC server at the given address and port. diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h index 268157e705..2f450cb36b 100644 --- a/libs/binder/include/binder/Trace.h +++ b/libs/binder/include/binder/Trace.h @@ -41,6 +41,7 @@ namespace os { // libcutils/libutils void trace_begin(uint64_t tag, const char* name); void trace_end(uint64_t tag); +void trace_int(uint64_t tag, const char* name, int32_t value); } // namespace os class LIBBINDER_EXPORTED ScopedTrace { diff --git a/libs/binder/liblog_stub/Android.bp b/libs/binder/liblog_stub/Android.bp index f2ca22fdb0..2de6658419 100644 --- a/libs/binder/liblog_stub/Android.bp +++ b/libs/binder/liblog_stub/Android.bp @@ -30,6 +30,7 @@ cc_library_headers { product_available: true, recovery_available: true, vendor_available: true, + cmake_snapshot_supported: true, target: { windows: { diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 9a2d14af32..26c228d9a4 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -32,17 +32,11 @@ license { ], } -cc_library { - name: "libbinder_ndk", - +cc_defaults { + name: "libbinder_ndk_common_defaults", host_supported: true, recovery_available: true, - llndk: { - symbol_file: "libbinder_ndk.map.txt", - export_llndk_headers: ["libvendorsupport_llndk_headers"], - }, - export_include_dirs: [ "include_cpp", "include_ndk", @@ -50,7 +44,6 @@ cc_library { ], cflags: [ - "-DBINDER_WITH_KERNEL_IPC", "-Wall", "-Wextra", "-Wextra-semi", @@ -59,14 +52,48 @@ cc_library { srcs: [ "ibinder.cpp", - "ibinder_jni.cpp", "libbinder.cpp", "parcel.cpp", + "stability.cpp", + "status.cpp", + ], +} + +cc_library_host_shared { + name: "libbinder_ndk_sdk", + + defaults: [ + "libbinder_ndk_common_defaults", + "binder_sdk_defaults", + ], + cmake_snapshot_supported: true, + + shared_libs: [ + "libbinder_sdk", + "libutils_binder_sdk", + ], +} + +cc_library { + name: "libbinder_ndk", + + defaults: ["libbinder_ndk_common_defaults"], + cmake_snapshot_supported: false, + + llndk: { + symbol_file: "libbinder_ndk.map.txt", + export_llndk_headers: ["libvendorsupport_llndk_headers"], + }, + + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], + + srcs: [ + "ibinder_jni.cpp", "parcel_jni.cpp", "persistable_bundle.cpp", "process.cpp", - "stability.cpp", - "status.cpp", "service_manager.cpp", ], @@ -195,6 +222,7 @@ cc_library_headers { host_supported: true, // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, + cmake_snapshot_supported: true, target: { darwin: { enabled: false, diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 52edae4a38..41b30a0a0f 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -30,7 +30,11 @@ enum AServiceManager_AddServiceFlag : uint32_t { * Services with methods that perform file IO, web socket creation or ways to egress data must * not be added with this flag for privacy concerns. */ - ADD_SERVICE_ALLOW_ISOLATED = 1, + ADD_SERVICE_ALLOW_ISOLATED = 1 << 0, + ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1, + ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2, + ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3, + ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4, }; /** diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 5529455cc6..4436dbeed7 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -49,7 +49,25 @@ binder_exception_t AServiceManager_addServiceWithFlags(AIBinder* binder, const c sp<IServiceManager> sm = defaultServiceManager(); bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED; - status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated); + int dumpFlags = 0; + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_HIGH; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_NORMAL; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; + } + if (dumpFlags == 0) { + dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; + } + status_t exception = + sm->addService(String16(instance), binder->getBinder(), allowIsolated, dumpFlags); + return PruneException(exception); } diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index 2d1175be75..f5b00718ee 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -114,7 +114,6 @@ rust_bindgen { crate_name: "binder_rs_serialization_bindgen", wrapper_src: "serialization.hpp", source_stem: "bindings", - cpp_std: "gnu++17", bindgen_flags: [ "--allowlist-type", "Transaction", "--allowlist-var", "TESTDATA_.*", diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index bd24a20370..3fe55d65f1 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -25,6 +25,7 @@ package { cc_defaults { name: "binder_test_defaults", + cmake_snapshot_supported: true, cflags: [ "-Wall", "-Werror", @@ -62,6 +63,7 @@ cc_test { "binderStatusUnitTest.cpp", "binderMemoryHeapBaseUnitTest.cpp", "binderRecordedTransactionTest.cpp", + "binderPersistableBundleTest.cpp", ], shared_libs: [ "libbinder", @@ -141,6 +143,7 @@ aidl_interface { name: "binderRpcTestIface", vendor_available: true, host_supported: true, + cmake_snapshot_supported: true, unstable: true, srcs: [ "BinderRpcTestClientInfo.aidl", @@ -222,6 +225,7 @@ cc_library_static { cc_defaults { name: "binderRpcTest_common_defaults", host_supported: true, + cmake_snapshot_supported: true, target: { darwin: { enabled: false, @@ -381,6 +385,9 @@ cc_test { static_libs: [ "libbinder_rpc_single_threaded_no_kernel", ], + shared_libs: [ + "libbinder_ndk", + ], } cc_binary { @@ -461,6 +468,20 @@ cc_test { } cc_test { + name: "binderRpcTestNoKernelAtAll", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], + cflags: [ + "-DBINDER_NO_KERNEL_IPC_TESTING", + ], +} + +cc_test { name: "binderRpcTestSingleThreaded", defaults: [ "binderRpcTest_defaults", @@ -487,6 +508,9 @@ cc_test { static_libs: [ "libbinder_rpc_single_threaded_no_kernel", ], + shared_libs: [ + "libbinder_ndk", + ], } cc_test { diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 9788d9d1dd..00406edcf2 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -38,6 +38,7 @@ #include <binder/IServiceManager.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <binder/Status.h> #include <binder/unique_fd.h> #include <utils/Flattenable.h> @@ -57,6 +58,7 @@ using namespace std::string_literals; using namespace std::chrono_literals; using android::base::testing::HasValue; using android::base::testing::Ok; +using android::binder::Status; using android::binder::unique_fd; using testing::ExplainMatchResult; using testing::Matcher; @@ -253,7 +255,7 @@ class BinderLibTest : public ::testing::Test { public: virtual void SetUp() { m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); - IPCThreadState::self()->restoreCallingWorkSource(0); + IPCThreadState::self()->restoreCallingWorkSource(0); } virtual void TearDown() { } @@ -461,6 +463,35 @@ TEST_F(BinderLibTest, AddManagerToManager) { EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder)); } +TEST_F(BinderLibTest, RegisterForNotificationsFailure) { + auto sm = defaultServiceManager(); + using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; + class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { + void onServiceRegistration(const String16&, const sp<IBinder>&) override {} + virtual ~LocalRegistrationCallbackImpl() {} + }; + sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + + EXPECT_EQ(BAD_VALUE, sm->registerForNotifications(String16("ValidName"), nullptr)); + EXPECT_EQ(UNKNOWN_ERROR, sm->registerForNotifications(String16("InvalidName!$"), cb)); +} + +TEST_F(BinderLibTest, UnregisterForNotificationsFailure) { + auto sm = defaultServiceManager(); + using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; + class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { + void onServiceRegistration(const String16&, const sp<IBinder>&) override {} + virtual ~LocalRegistrationCallbackImpl() {} + }; + sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + + EXPECT_EQ(OK, sm->registerForNotifications(String16("ValidName"), cb)); + + EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("ValidName"), nullptr)); + EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("AnotherValidName"), cb)); + EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("InvalidName!!!"), cb)); +} + TEST_F(BinderLibTest, WasParceled) { auto binder = sp<BBinder>::make(); EXPECT_FALSE(binder->wasParceled()); diff --git a/libs/binder/tests/binderPersistableBundleTest.cpp b/libs/binder/tests/binderPersistableBundleTest.cpp new file mode 100644 index 0000000000..392e018735 --- /dev/null +++ b/libs/binder/tests/binderPersistableBundleTest.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Parcel.h> +#include <binder/PersistableBundle.h> +#include <gtest/gtest.h> +#include <numeric> + +using android::OK; +using android::Parcel; +using android::status_t; +using android::String16; +using android::String8; +using android::os::PersistableBundle; + +namespace android { + +inline std::string to_string(String16 const& str) { + return String8{str}.c_str(); +} + +namespace os { + +template <typename T> +inline std::ostream& operator<<(std::ostream& out, std::vector<T> const& vec) { + using std::to_string; + auto str = + std::accumulate(vec.begin(), vec.end(), std::string{}, + [](std::string const& a, auto const& b) { return a + to_string(b); }); + return out << str; +} + +inline std::ostream& operator<<(std::ostream& out, PersistableBundle const& pb) { +#define PRINT(TYPENAME, TYPE) \ + for (auto const& key : pb.get##TYPENAME##Keys()) { \ + TYPE val{}; \ + pb.get##TYPENAME(key, &val); \ + out << #TYPE " " << key << ": " << val << std::endl; \ + } + + out << "size: " << pb.size() << std::endl; + PRINT(Boolean, bool); + PRINT(Int, int); + PRINT(Long, int64_t); + PRINT(Double, double); + PRINT(String, String16); + PRINT(BooleanVector, std::vector<bool>); + PRINT(IntVector, std::vector<int32_t>); + PRINT(LongVector, std::vector<int64_t>); + PRINT(DoubleVector, std::vector<double>); + PRINT(StringVector, std::vector<String16>); + PRINT(PersistableBundle, PersistableBundle); + +#undef PRINT + + return out; +} + +} // namespace os +} // namespace android + +static const String16 kKey{"key"}; + +static PersistableBundle createSimplePersistableBundle() { + PersistableBundle pb{}; + pb.putInt(kKey, 64); + return pb; +} + +#define TEST_PUT_AND_GET(TYPENAME, TYPE, ...) \ + TEST(PersistableBundle, PutAndGet##TYPENAME) { \ + TYPE const expected{__VA_ARGS__}; \ + PersistableBundle pb{}; \ + \ + pb.put##TYPENAME(kKey, expected); \ + \ + std::set<String16> expectedKeys{kKey}; \ + EXPECT_EQ(pb.get##TYPENAME##Keys(), expectedKeys); \ + \ + TYPE val{}; \ + EXPECT_TRUE(pb.get##TYPENAME(kKey, &val)); \ + EXPECT_EQ(val, expected); \ + } + +TEST_PUT_AND_GET(Boolean, bool, true); +TEST_PUT_AND_GET(Int, int, 64); +TEST_PUT_AND_GET(Long, int64_t, 42); +TEST_PUT_AND_GET(Double, double, 42.64); +TEST_PUT_AND_GET(String, String16, String16{"foo"}); +TEST_PUT_AND_GET(BooleanVector, std::vector<bool>, true, true); +TEST_PUT_AND_GET(IntVector, std::vector<int32_t>, 1, 2); +TEST_PUT_AND_GET(LongVector, std::vector<int64_t>, 1, 2); +TEST_PUT_AND_GET(DoubleVector, std::vector<double>, 4.2, 5.9); +TEST_PUT_AND_GET(StringVector, std::vector<String16>, String16{"foo"}, String16{"bar"}); +TEST_PUT_AND_GET(PersistableBundle, PersistableBundle, createSimplePersistableBundle()); + +TEST(PersistableBundle, ParcelAndUnparcel) { + PersistableBundle expected = createSimplePersistableBundle(); + PersistableBundle out{}; + + Parcel p{}; + EXPECT_EQ(expected.writeToParcel(&p), 0); + p.setDataPosition(0); + EXPECT_EQ(out.readFromParcel(&p), 0); + + EXPECT_EQ(expected, out); +} + +TEST(PersistableBundle, OverwriteKey) { + PersistableBundle pb{}; + + pb.putInt(kKey, 64); + pb.putDouble(kKey, 0.5); + + EXPECT_EQ(pb.getIntKeys().size(), 0); + EXPECT_EQ(pb.getDoubleKeys().size(), 1); + + double out; + EXPECT_TRUE(pb.getDouble(kKey, &out)); + EXPECT_EQ(out, 0.5); +} diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index c044d3943d..19882ea1a9 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -1278,7 +1278,7 @@ static std::vector<BinderRpc::ParamType> getBinderRpcParams() { for (const auto& clientVersion : testVersions()) { for (const auto& serverVersion : testVersions()) { for (bool singleThreaded : {false, true}) { - for (bool noKernel : {false, true}) { + for (bool noKernel : noKernelValues()) { ret.push_back(BinderRpc::ParamType{ .type = type, .security = security, @@ -1299,7 +1299,7 @@ static std::vector<BinderRpc::ParamType> getBinderRpcParams() { .clientVersion = RPC_WIRE_PROTOCOL_VERSION, .serverVersion = RPC_WIRE_PROTOCOL_VERSION, .singleThreaded = false, - .noKernel = false, + .noKernel = !kEnableKernelIpcTesting, }); } } diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index acc0373bf4..dc22647b85 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -64,6 +64,12 @@ namespace android { +#ifdef BINDER_NO_KERNEL_IPC_TESTING +constexpr bool kEnableKernelIpcTesting = false; +#else +constexpr bool kEnableKernelIpcTesting = true; +#endif + constexpr char kLocalInetAddress[] = "127.0.0.1"; enum class RpcSecurity { RAW, TLS }; @@ -72,6 +78,14 @@ static inline std::vector<RpcSecurity> RpcSecurityValues() { return {RpcSecurity::RAW, RpcSecurity::TLS}; } +static inline std::vector<bool> noKernelValues() { + std::vector<bool> values = {true}; + if (kEnableKernelIpcTesting) { + values.push_back(false); + } + return values; +} + static inline bool hasExperimentalRpc() { #ifdef BINDER_RPC_TO_TRUSTY_TEST // Trusty services do not support the experimental version, diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index e59dc82b2b..91145f0c13 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -14,14 +14,15 @@ * limitations under the License. */ -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android-base/strings.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> #include <binder/Status.h> #include <gtest/gtest.h> +#ifdef __ANDROID__ +#include <android-base/properties.h> +#endif + #include "../Debug.h" #include "../Utils.h" @@ -69,8 +70,8 @@ static const std::vector<std::function<void(Parcel* p)>> kFillFuns { [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"a"))); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"baba"))); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeStrongBinder(nullptr)); }, - [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(arraysize(kInt32Array), kInt32Array)); }, - [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(arraysize(kByteArray), kByteArray)); }, + [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(countof(kInt32Array), kInt32Array)); }, + [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(countof(kByteArray), kByteArray)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(true)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(false)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeChar('a')); }, @@ -162,8 +163,8 @@ static const std::vector<std::function<void(Parcel* p)>> kFillFuns { static void setParcelForRpc(Parcel* p, uint32_t version) { auto session = RpcSession::make(); - CHECK(session->setProtocolVersion(version)); - CHECK_EQ(OK, session->addNullDebuggingClient()); + EXPECT_TRUE(session->setProtocolVersion(version)); + EXPECT_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); } @@ -180,13 +181,25 @@ static std::string buildRepr(uint32_t version) { return result; } +// To be replaced with std::views::split (and std::views::zip) once C++ compilers catch up. +static std::vector<std::string> split(std::string_view s, char delimiter) { + std::vector<std::string> result; + size_t pos = 0; + while (true) { + const auto found = s.find(delimiter, pos); + result.emplace_back(s.substr(pos, found - pos)); + if (found == s.npos) return result; + pos = found + 1; + } +} + static void checkRepr(const std::string& repr, uint32_t version) { const std::string actualRepr = buildRepr(version); - auto expected = base::Split(repr, "|"); + auto expected = split(repr, '|'); ASSERT_EQ(expected.size(), kFillFuns.size()); - auto actual = base::Split(actualRepr, "|"); + auto actual = split(actualRepr, '|'); ASSERT_EQ(actual.size(), kFillFuns.size()); for (size_t i = 0; i < kFillFuns.size(); i++) { @@ -257,8 +270,13 @@ TEST(RpcWire, NextIsPlusOneReminder) { TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) { if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { - EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL") - << "Binder RPC wire protocol must be frozen on a release branch!"; +#ifdef __ANDROID__ + bool isRelease = base::GetProperty("ro.build.version.codename", "") == "REL"; +#else + bool isRelease = true; +#endif + EXPECT_FALSE(isRelease) + << "Binder RPC wire protocol must be frozen in release configuration!"; } } diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index 09db32609a..157ab3c85d 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -40,6 +40,8 @@ void trace_begin(uint64_t, const char*) {} void trace_end(uint64_t) {} +void trace_int(uint64_t, const char*, int32_t) {} + uint64_t GetThreadId() { return 0; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index ed1701460a..d782f42071 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -113,6 +113,27 @@ rust_bindgen { "--allowlist-var=AINPUT_SOURCE_HDMI", "--allowlist-var=AINPUT_SOURCE_SENSOR", "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC", + "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC", + "--allowlist-var=AMETA_NONE", + "--allowlist-var=AMETA_ALT_ON", + "--allowlist-var=AMETA_ALT_LEFT_ON", + "--allowlist-var=AMETA_ALT_RIGHT_ON", + "--allowlist-var=AMETA_SHIFT_ON", + "--allowlist-var=AMETA_SHIFT_LEFT_ON", + "--allowlist-var=AMETA_SHIFT_RIGHT_ON", + "--allowlist-var=AMETA_SYM_ON", + "--allowlist-var=AMETA_FUNCTION_ON", + "--allowlist-var=AMETA_CTRL_ON", + "--allowlist-var=AMETA_CTRL_LEFT_ON", + "--allowlist-var=AMETA_CTRL_RIGHT_ON", + "--allowlist-var=AMETA_META_ON", + "--allowlist-var=AMETA_META_LEFT_ON", + "--allowlist-var=AMETA_META_RIGHT_ON", + "--allowlist-var=AMETA_CAPS_LOCK_ON", + "--allowlist-var=AMETA_NUM_LOCK_ON", + "--allowlist-var=AMETA_SCROLL_LOCK_ON", ], static_libs: [ @@ -204,6 +225,7 @@ cc_library { "InputVerifier.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", + "KeyboardClassifier.cpp", "KeyLayoutMap.cpp", "MotionPredictor.cpp", "MotionPredictorMetricsManager.cpp", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index ee121d53fe..b09814797f 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -96,6 +96,19 @@ int32_t resolveActionForSplitMotionEvent( return AMOTION_EVENT_ACTION_DOWN; } +float transformOrientation(const ui::Transform& transform, const PointerCoords& coords, + int32_t motionEventFlags) { + if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) == 0) { + return 0; + } + + const bool isDirectionalAngle = + (motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION) != 0; + + return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + isDirectionalAngle); +} + } // namespace const char* motionClassificationToString(MotionClassification classification) { @@ -187,7 +200,7 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) return roundTransformedCoords(transformedXy - transformedOrigin); } -float transformAngle(const ui::Transform& transform, float angleRadians) { +float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. float x = sinf(angleRadians); @@ -201,6 +214,11 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { transformedPoint.x -= origin.x; transformedPoint.y -= origin.y; + if (!isDirectional && transformedPoint.y > 0) { + // Limit the range of atan2f to [-pi/2, pi/2] by reversing the direction of the vector. + transformedPoint *= -1; + } + // Derive the transformed vector's clockwise angle from vertical. // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API. return atan2f(transformedPoint.x, -transformedPoint.y); @@ -530,26 +548,6 @@ bool PointerCoords::operator==(const PointerCoords& other) const { return true; } -void PointerCoords::transform(const ui::Transform& transform) { - const vec2 xy = transform.transform(getXYValue()); - setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); - setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); - - if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) || - BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) { - const ui::Transform rotation(transform.getOrientation()); - const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); - setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); - setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); - } - - if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) { - const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val)); - } -} - // --- MotionEvent --- void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, @@ -723,13 +721,13 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords); + return calculateTransformedAxisValue(axis, mSource, mFlags, mRawTransform, coords); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - return calculateTransformedAxisValue(axis, mSource, mTransform, coords); + return calculateTransformedAxisValue(axis, mSource, mFlags, mTransform, coords); } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -786,8 +784,9 @@ void MotionEvent::applyTransform(const std::array<float, 9>& matrix) { transform.set(matrix); // Apply the transformation to all samples. - std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), - [&transform](PointerCoords& c) { c.transform(transform); }); + std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), [&](PointerCoords& c) { + calculateTransformedCoordsInPlace(c, mSource, mFlags, transform); + }); if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION && mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) { @@ -1059,7 +1058,7 @@ vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& t } // Keep in sync with calculateTransformedCoords. -float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, +float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags, const ui::Transform& transform, const PointerCoords& coords) { if (shouldDisregardTransformation(source)) { @@ -1081,7 +1080,7 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, } if (axis == AMOTION_EVENT_AXIS_ORIENTATION) { - return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + return transformOrientation(transform, coords, flags); } return coords.getAxisValue(axis); @@ -1089,29 +1088,32 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, // Keep in sync with calculateTransformedAxisValue. This is an optimization of // calculateTransformedAxisValue for all PointerCoords axes. -PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, - const ui::Transform& transform, - const PointerCoords& coords) { +void MotionEvent::calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source, + int32_t flags, const ui::Transform& transform) { if (shouldDisregardTransformation(source)) { - return coords; + return; } - PointerCoords out = coords; const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue()); - out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); const vec2 relativeXy = transformWithoutTranslation(transform, {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); - out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); - out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(transform, - coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION))); + coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + transformOrientation(transform, coords, flags)); +} +PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, int32_t flags, + const ui::Transform& transform, + const PointerCoords& coords) { + PointerCoords out = coords; + calculateTransformedCoordsInPlace(out, source, flags, transform); return out; } diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp index abc039281a..fcf490d5f9 100644 --- a/libs/input/InputConsumer.cpp +++ b/libs/input/InputConsumer.cpp @@ -181,7 +181,8 @@ inline bool isPointerEvent(int32_t source) { } bool shouldResampleTool(ToolType toolType) { - return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; + return toolType == ToolType::FINGER || toolType == ToolType::MOUSE || + toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN; } } // namespace @@ -592,6 +593,11 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id); return; } + if (!shouldResampleTool(event->getToolType(i))) { + ALOGD_IF(debugResampling(), + "Not resampled, containing unsupported tool type at pointer %d", id); + return; + } } // Find the data to use for resampling. @@ -639,10 +645,18 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, } if (current->eventTime == sampleTime) { - // Prevents having 2 events with identical times and coordinates. + ALOGD_IF(debugResampling(), "Not resampled, 2 events with identical times."); return; } + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!other->idBits.hasBit(id)) { + ALOGD_IF(debugResampling(), "Not resampled, the other doesn't have pointer id %d.", id); + return; + } + } + // Resample touch coordinates. History oldLastResample; oldLastResample.initializeFrom(touchState.lastResample); @@ -670,22 +684,16 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, const PointerCoords& currentCoords = current->getPointerById(id); resampledCoords = currentCoords; resampledCoords.isResampled = true; - if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { - const PointerCoords& otherCoords = other->getPointerById(id); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, - lerp(currentCoords.getX(), otherCoords.getX(), alpha)); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, - lerp(currentCoords.getY(), otherCoords.getY(), alpha)); - ALOGD_IF(debugResampling(), - "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " - "other (%0.3f, %0.3f), alpha %0.3f", - id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); - } else { - ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, - resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY()); - } + const PointerCoords& otherCoords = other->getPointerById(id); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); + ALOGD_IF(debugResampling(), + "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); } event->addSample(sampleTime, touchState.lastResample.pointers); diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index bc678103c2..9333ab83a6 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -273,10 +273,7 @@ void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { } void InputDeviceInfo::setKeyboardType(int32_t keyboardType) { - static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); - static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC); - // There can be multiple subdevices with different keyboard types, set it to the highest type - mKeyboardType = std::max(mKeyboardType, keyboardType); + mKeyboardType = keyboardType; } void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) { diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp new file mode 100644 index 0000000000..0c2c7be582 --- /dev/null +++ b/libs/input/KeyboardClassifier.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "KeyboardClassifier" + +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <ftl/flags.h> +#include <input/KeyboardClassifier.h> + +#include "input_cxx_bridge.rs.h" + +namespace input_flags = com::android::input::flags; + +using android::input::RustInputDeviceIdentifier; + +namespace android { + +KeyboardClassifier::KeyboardClassifier() { + if (input_flags::enable_keyboard_classifier()) { + mRustClassifier = android::input::keyboardClassifier::create(); + } +} + +KeyboardType KeyboardClassifier::getKeyboardType(DeviceId deviceId) { + if (mRustClassifier) { + return static_cast<KeyboardType>( + android::input::keyboardClassifier::getKeyboardType(**mRustClassifier, deviceId)); + } else { + auto it = mKeyboardTypeMap.find(deviceId); + if (it == mKeyboardTypeMap.end()) { + return KeyboardType::NONE; + } + return it->second; + } +} + +// Copied from EventHub.h +const uint32_t DEVICE_CLASS_KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD; +const uint32_t DEVICE_CLASS_ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY; + +void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId, + const InputDeviceIdentifier& identifier, + uint32_t deviceClasses) { + if (mRustClassifier) { + RustInputDeviceIdentifier rustIdentifier; + rustIdentifier.name = identifier.name; + rustIdentifier.location = identifier.location; + rustIdentifier.unique_id = identifier.uniqueId; + rustIdentifier.bus = identifier.bus; + rustIdentifier.vendor = identifier.vendor; + rustIdentifier.product = identifier.product; + rustIdentifier.version = identifier.version; + rustIdentifier.descriptor = identifier.descriptor; + android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId, + rustIdentifier, deviceClasses); + } else { + bool isKeyboard = (deviceClasses & DEVICE_CLASS_KEYBOARD) != 0; + bool hasAlphabeticKey = (deviceClasses & DEVICE_CLASS_ALPHAKEY) != 0; + mKeyboardTypeMap.insert_or_assign(deviceId, + isKeyboard ? (hasAlphabeticKey + ? KeyboardType::ALPHABETIC + : KeyboardType::NON_ALPHABETIC) + : KeyboardType::NONE); + } +} + +void KeyboardClassifier::processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState) { + if (mRustClassifier && + !android::input::keyboardClassifier::isFinalized(**mRustClassifier, deviceId)) { + android::input::keyboardClassifier::processKey(**mRustClassifier, deviceId, evdevCode, + metaState); + } +} + +} // namespace android diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 90ed2b7d06..a77dfa59fe 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -116,6 +116,31 @@ interface IInputConstants const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40; /** + * This flag indicates that the event has a valid value for AXIS_ORIENTATION. + * + * This is a private flag that is not used in Java. + * @hide + */ + const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80; + + /** + * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine + * the direction in which the tool is pointing. The value of the orientation axis will be in + * the range [-pi, pi], which represents a full circle. This is usually supported by devices + * like styluses. + * + * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing + * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], + * which represents half a circle. This is usually the case for devices like touchscreens and + * touchpads, for which it is difficult to tell which direction along the major axis of the + * touch ellipse the finger is pointing. + * + * This is a private flag that is not used in Java. + * @hide + */ + const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100; + + /** * The input event was generated or modified by accessibility service. * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either * set of flags, including in input/Input.h and in android/input.h. @@ -234,4 +259,157 @@ interface IInputConstants * time to adjust to changes in direction. */ const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9; + + + /* + * Input device class: Keyboard + * The input device is a keyboard or has buttons. + * + * @hide + */ + const int DEVICE_CLASS_KEYBOARD = 0x00000001; + + /* + * Input device class: Alphakey + * The input device is an alpha-numeric keyboard (not just a dial pad). + * + * @hide + */ + const int DEVICE_CLASS_ALPHAKEY = 0x00000002; + + /* + * Input device class: Touch + * The input device is a touchscreen or a touchpad (either single-touch or multi-touch). + * + * @hide + */ + const int DEVICE_CLASS_TOUCH = 0x00000004; + + /* + * Input device class: Cursor + * The input device is a cursor device such as a trackball or mouse. + * + * @hide + */ + const int DEVICE_CLASS_CURSOR = 0x00000008; + + /* + * Input device class: Multi-touch + * The input device is a multi-touch touchscreen or touchpad. + * + * @hide + */ + const int DEVICE_CLASS_TOUCH_MT = 0x00000010; + + /* + * Input device class: Dpad + * The input device is a directional pad (implies keyboard, has DPAD keys). + * + * @hide + */ + const int DEVICE_CLASS_DPAD = 0x00000020; + + /* + * Input device class: Gamepad + * The input device is a gamepad (implies keyboard, has BUTTON keys). + * + * @hide + */ + const int DEVICE_CLASS_GAMEPAD = 0x00000040; + + /* + * Input device class: Switch + * The input device has switches. + * + * @hide + */ + const int DEVICE_CLASS_SWITCH = 0x00000080; + + /* + * Input device class: Joystick + * The input device is a joystick (implies gamepad, has joystick absolute axes). + * + * @hide + */ + const int DEVICE_CLASS_JOYSTICK = 0x00000100; + + /* + * Input device class: Vibrator + * The input device has a vibrator (supports FF_RUMBLE). + * + * @hide + */ + const int DEVICE_CLASS_VIBRATOR = 0x00000200; + + /* + * Input device class: Mic + * The input device has a microphone. + * + * @hide + */ + const int DEVICE_CLASS_MIC = 0x00000400; + + /* + * Input device class: External Stylus + * The input device is an external stylus (has data we want to fuse with touch data). + * + * @hide + */ + const int DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800; + + /* + * Input device class: Rotary Encoder + * The input device has a rotary encoder. + * + * @hide + */ + const int DEVICE_CLASS_ROTARY_ENCODER = 0x00001000; + + /* + * Input device class: Sensor + * The input device has a sensor like accelerometer, gyro, etc. + * + * @hide + */ + const int DEVICE_CLASS_SENSOR = 0x00002000; + + /* + * Input device class: Battery + * The input device has a battery. + * + * @hide + */ + const int DEVICE_CLASS_BATTERY = 0x00004000; + + /* + * Input device class: Light + * The input device has sysfs controllable lights. + * + * @hide + */ + const int DEVICE_CLASS_LIGHT = 0x00008000; + + /* + * Input device class: Touchpad + * The input device is a touchpad, requiring an on-screen cursor. + * + * @hide + */ + const int DEVICE_CLASS_TOUCHPAD = 0x00010000; + + /* + * Input device class: Virtual + * The input device is virtual (not a real device, not part of UI configuration). + * + * @hide + */ + const int DEVICE_CLASS_VIRTUAL = 0x20000000; + + /* + * Input device class: External + * The input device is external (not built-in). + * + * @hide + */ + const int DEVICE_CLASS_EXTERNAL = 0x40000000; } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 560166c2bd..a2192cbdc4 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -150,3 +150,10 @@ flag { description: "Hide touch and pointer indicators if a secure window is present on display" bug: "325252005" } + +flag { + name: "enable_keyboard_classifier" + namespace: "input" + description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" + bug: "263559234" +} diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index d0dbd6fa16..0574245fa6 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -16,22 +16,26 @@ //! Common definitions of the Android Input Framework in rust. +use crate::ffi::RustInputDeviceIdentifier; use bitflags::bitflags; -use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_CANCELED; -use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT; -use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; -use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; +use inputconstants::aidl::android::os::IInputConstants; use std::fmt; /// The InputDevice ID. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct DeviceId(pub i32); +/// The InputDevice equivalent for Rust inputflinger +#[derive(Debug)] +pub struct InputDevice { + /// InputDevice ID + pub device_id: DeviceId, + /// InputDevice unique identifier + pub identifier: RustInputDeviceIdentifier, + /// InputDevice classes (equivalent to EventHub InputDeviceClass) + pub classes: DeviceClass, +} + #[repr(u32)] pub enum SourceClass { None = input_bindgen::AINPUT_SOURCE_CLASS_NONE, @@ -192,23 +196,23 @@ bitflags! { #[derive(Debug)] pub struct MotionFlags: u32 { /// FLAG_WINDOW_IS_OBSCURED - const WINDOW_IS_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32; + const WINDOW_IS_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32; /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED - const WINDOW_IS_PARTIALLY_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32; + const WINDOW_IS_PARTIALLY_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32; /// FLAG_HOVER_EXIT_PENDING - const HOVER_EXIT_PENDING = MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32; + const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32; /// FLAG_IS_GENERATED_GESTURE - const IS_GENERATED_GESTURE = MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32; + const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32; /// FLAG_CANCELED - const CANCELED = INPUT_EVENT_FLAG_CANCELED as u32; + const CANCELED = IInputConstants::INPUT_EVENT_FLAG_CANCELED as u32; /// FLAG_NO_FOCUS_CHANGE - const NO_FOCUS_CHANGE = MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32; + const NO_FOCUS_CHANGE = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32; /// FLAG_IS_ACCESSIBILITY_EVENT - const IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32; + const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32; /// FLAG_TAINTED - const TAINTED = INPUT_EVENT_FLAG_TAINTED as u32; + const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32; /// FLAG_TARGET_ACCESSIBILITY_FOCUS - const TARGET_ACCESSIBILITY_FOCUS = MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32; + const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32; } } @@ -220,6 +224,107 @@ impl Source { } } +bitflags! { + /// Device class of the input device. These are duplicated from Eventhub.h + /// We need to make sure the two version remain in sync when adding new classes. + #[derive(Debug, PartialEq)] + pub struct DeviceClass: u32 { + /// The input device is a keyboard or has buttons + const Keyboard = IInputConstants::DEVICE_CLASS_KEYBOARD as u32; + /// The input device is an alpha-numeric keyboard (not just a dial pad) + const AlphabeticKey = IInputConstants::DEVICE_CLASS_ALPHAKEY as u32; + /// The input device is a touchscreen or a touchpad (either single-touch or multi-touch) + const Touch = IInputConstants::DEVICE_CLASS_TOUCH as u32; + /// The input device is a cursor device such as a trackball or mouse. + const Cursor = IInputConstants::DEVICE_CLASS_CURSOR as u32; + /// The input device is a multi-touch touchscreen or touchpad. + const MultiTouch = IInputConstants::DEVICE_CLASS_TOUCH_MT as u32; + /// The input device is a directional pad (implies keyboard, has DPAD keys). + const Dpad = IInputConstants::DEVICE_CLASS_DPAD as u32; + /// The input device is a gamepad (implies keyboard, has BUTTON keys). + const Gamepad = IInputConstants::DEVICE_CLASS_GAMEPAD as u32; + /// The input device has switches. + const Switch = IInputConstants::DEVICE_CLASS_SWITCH as u32; + /// The input device is a joystick (implies gamepad, has joystick absolute axes). + const Joystick = IInputConstants::DEVICE_CLASS_JOYSTICK as u32; + /// The input device has a vibrator (supports FF_RUMBLE). + const Vibrator = IInputConstants::DEVICE_CLASS_VIBRATOR as u32; + /// The input device has a microphone. + const Mic = IInputConstants::DEVICE_CLASS_MIC as u32; + /// The input device is an external stylus (has data we want to fuse with touch data). + const ExternalStylus = IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS as u32; + /// The input device has a rotary encoder + const RotaryEncoder = IInputConstants::DEVICE_CLASS_ROTARY_ENCODER as u32; + /// The input device has a sensor like accelerometer, gyro, etc + const Sensor = IInputConstants::DEVICE_CLASS_SENSOR as u32; + /// The input device has a battery + const Battery = IInputConstants::DEVICE_CLASS_BATTERY as u32; + /// The input device has sysfs controllable lights + const Light = IInputConstants::DEVICE_CLASS_LIGHT as u32; + /// The input device is a touchpad, requiring an on-screen cursor. + const Touchpad = IInputConstants::DEVICE_CLASS_TOUCHPAD as u32; + /// The input device is virtual (not a real device, not part of UI configuration). + const Virtual = IInputConstants::DEVICE_CLASS_VIRTUAL as u32; + /// The input device is external (not built-in). + const External = IInputConstants::DEVICE_CLASS_EXTERNAL as u32; + } +} + +bitflags! { + /// Modifier state flags + #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] + pub struct ModifierState: u32 { + /// No meta keys are pressed + const None = input_bindgen::AMETA_NONE; + /// This mask is used to check whether one of the ALT meta keys is pressed + const AltOn = input_bindgen::AMETA_ALT_ON; + /// This mask is used to check whether the left ALT meta key is pressed + const AltLeftOn = input_bindgen::AMETA_ALT_LEFT_ON; + /// This mask is used to check whether the right ALT meta key is pressed + const AltRightOn = input_bindgen::AMETA_ALT_RIGHT_ON; + /// This mask is used to check whether one of the SHIFT meta keys is pressed + const ShiftOn = input_bindgen::AMETA_SHIFT_ON; + /// This mask is used to check whether the left SHIFT meta key is pressed + const ShiftLeftOn = input_bindgen::AMETA_SHIFT_LEFT_ON; + /// This mask is used to check whether the right SHIFT meta key is pressed + const ShiftRightOn = input_bindgen::AMETA_SHIFT_RIGHT_ON; + /// This mask is used to check whether the SYM meta key is pressed + const SymOn = input_bindgen::AMETA_SYM_ON; + /// This mask is used to check whether the FUNCTION meta key is pressed + const FunctionOn = input_bindgen::AMETA_FUNCTION_ON; + /// This mask is used to check whether one of the CTRL meta keys is pressed + const CtrlOn = input_bindgen::AMETA_CTRL_ON; + /// This mask is used to check whether the left CTRL meta key is pressed + const CtrlLeftOn = input_bindgen::AMETA_CTRL_LEFT_ON; + /// This mask is used to check whether the right CTRL meta key is pressed + const CtrlRightOn = input_bindgen::AMETA_CTRL_RIGHT_ON; + /// This mask is used to check whether one of the META meta keys is pressed + const MetaOn = input_bindgen::AMETA_META_ON; + /// This mask is used to check whether the left META meta key is pressed + const MetaLeftOn = input_bindgen::AMETA_META_LEFT_ON; + /// This mask is used to check whether the right META meta key is pressed + const MetaRightOn = input_bindgen::AMETA_META_RIGHT_ON; + /// This mask is used to check whether the CAPS LOCK meta key is on + const CapsLockOn = input_bindgen::AMETA_CAPS_LOCK_ON; + /// This mask is used to check whether the NUM LOCK meta key is on + const NumLockOn = input_bindgen::AMETA_NUM_LOCK_ON; + /// This mask is used to check whether the SCROLL LOCK meta key is on + const ScrollLockOn = input_bindgen::AMETA_SCROLL_LOCK_ON; + } +} + +/// A rust enum representation of a Keyboard type. +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum KeyboardType { + /// KEYBOARD_TYPE_NONE + None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE, + /// KEYBOARD_TYPE_NON_ALPHABETIC + NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, + /// KEYBOARD_TYPE_ALPHABETIC + Alphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_ALPHABETIC, +} + #[cfg(test)] mod tests { use crate::input::SourceClass; diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs new file mode 100644 index 0000000000..1063fac664 --- /dev/null +++ b/libs/input/rust/keyboard_classifier.rs @@ -0,0 +1,345 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Contains the KeyboardClassifier, that tries to identify whether an Input device is an +//! alphabetic or non-alphabetic keyboard. It also tracks the KeyEvents produced by the device +//! in order to verify/change the inferred keyboard type. +//! +//! Initial classification: +//! - If DeviceClass includes Dpad, Touch, Cursor, MultiTouch, ExternalStylus, Touchpad, Dpad, +//! Gamepad, Switch, Joystick, RotaryEncoder => KeyboardType::NonAlphabetic +//! - Otherwise if DeviceClass has Keyboard and not AlphabeticKey => KeyboardType::NonAlphabetic +//! - Otherwise if DeviceClass has both Keyboard and AlphabeticKey => KeyboardType::Alphabetic +//! +//! On process keys: +//! - If KeyboardType::NonAlphabetic and we receive alphabetic key event, then change type to +//! KeyboardType::Alphabetic. Once changed, no further changes. (i.e. verified = true) +//! - TODO(b/263559234): If KeyboardType::Alphabetic and we don't receive any alphabetic key event +//! across multiple device connections in a time period, then change type to +//! KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic +//! (i.e. verified = false). +//! +//! TODO(b/263559234): Data store implementation to store information about past classification + +use crate::input::{DeviceId, InputDevice, KeyboardType}; +use crate::{DeviceClass, ModifierState}; +use std::collections::HashMap; + +/// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic +/// keyboard or non-alphabetic keyboard +#[derive(Default)] +pub struct KeyboardClassifier { + device_map: HashMap<DeviceId, KeyboardInfo>, +} + +struct KeyboardInfo { + _device: InputDevice, + keyboard_type: KeyboardType, + is_finalized: bool, +} + +impl KeyboardClassifier { + /// Create a new KeyboardClassifier + pub fn new() -> Self { + Default::default() + } + + /// Adds keyboard to KeyboardClassifier + pub fn notify_keyboard_changed(&mut self, device: InputDevice) { + let (keyboard_type, is_finalized) = self.classify_keyboard(&device); + self.device_map.insert( + device.device_id, + KeyboardInfo { _device: device, keyboard_type, is_finalized }, + ); + } + + /// Get keyboard type for a tracked keyboard in KeyboardClassifier + pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType { + return if let Some(keyboard) = self.device_map.get(&device_id) { + keyboard.keyboard_type + } else { + KeyboardType::None + }; + } + + /// Tells if keyboard type classification is finalized. Once finalized the classification can't + /// change until device is reconnected again. + /// + /// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or + /// allowlist that are explicitly categorized and won't change with future key events + pub fn is_finalized(&self, device_id: DeviceId) -> bool { + return if let Some(keyboard) = self.device_map.get(&device_id) { + keyboard.is_finalized + } else { + false + }; + } + + /// Process a key event and change keyboard type if required. + /// - If any key event occurs, the keyboard type will change from None to NonAlphabetic + /// - If an alphabetic key occurs, the keyboard type will change to Alphabetic + pub fn process_key( + &mut self, + device_id: DeviceId, + evdev_code: i32, + modifier_state: ModifierState, + ) { + if let Some(keyboard) = self.device_map.get_mut(&device_id) { + // Ignore all key events with modifier state since they can be macro shortcuts used by + // some non-keyboard peripherals like TV remotes, game controllers, etc. + if modifier_state.bits() != 0 { + return; + } + if Self::is_alphabetic_key(&evdev_code) { + keyboard.keyboard_type = KeyboardType::Alphabetic; + keyboard.is_finalized = true; + } + } + } + + fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) { + // This should never happen but having keyboard device class is necessary to be classified + // as any type of keyboard. + if !device.classes.contains(DeviceClass::Keyboard) { + return (KeyboardType::None, true); + } + // Normal classification for internal and virtual keyboards + if !device.classes.contains(DeviceClass::External) + || device.classes.contains(DeviceClass::Virtual) + { + return if device.classes.contains(DeviceClass::AlphabeticKey) { + (KeyboardType::Alphabetic, true) + } else { + (KeyboardType::NonAlphabetic, true) + }; + } + // Any composite device with multiple device classes should be categorized as non-alphabetic + // keyboard initially + if device.classes.contains(DeviceClass::Touch) + || device.classes.contains(DeviceClass::Cursor) + || device.classes.contains(DeviceClass::MultiTouch) + || device.classes.contains(DeviceClass::ExternalStylus) + || device.classes.contains(DeviceClass::Touchpad) + || device.classes.contains(DeviceClass::Dpad) + || device.classes.contains(DeviceClass::Gamepad) + || device.classes.contains(DeviceClass::Switch) + || device.classes.contains(DeviceClass::Joystick) + || device.classes.contains(DeviceClass::RotaryEncoder) + { + // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the + // kernel, we no longer need to process key events to verify. + return ( + KeyboardType::NonAlphabetic, + !device.classes.contains(DeviceClass::AlphabeticKey), + ); + } + // Only devices with "Keyboard" and "AlphabeticKey" should be classified as full keyboard + if device.classes.contains(DeviceClass::AlphabeticKey) { + (KeyboardType::Alphabetic, true) + } else { + // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the + // kernel, we no longer need to process key events to verify. + (KeyboardType::NonAlphabetic, true) + } + } + + fn is_alphabetic_key(evdev_code: &i32) -> bool { + // Keyboard alphabetic row 1 (Q W E R T Y U I O P [ ]) + (16..=27).contains(evdev_code) + // Keyboard alphabetic row 2 (A S D F G H J K L ; ' `) + || (30..=41).contains(evdev_code) + // Keyboard alphabetic row 3 (\ Z X C V B N M , . /) + || (43..=53).contains(evdev_code) + } +} + +#[cfg(test)] +mod tests { + use crate::input::{DeviceId, InputDevice, KeyboardType}; + use crate::keyboard_classifier::KeyboardClassifier; + use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier}; + + static DEVICE_ID: DeviceId = DeviceId(1); + static KEY_A: i32 = 30; + static KEY_1: i32 = 2; + + #[test] + fn classify_external_alphabetic_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic); + assert!(classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_external_non_alphabetic_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier + .notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External)); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_mouse_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Cursor + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_touchpad_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Touchpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_stylus_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::ExternalStylus + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_dpad_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_joystick_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Joystick + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn classify_gamepad_pretending_as_keyboard() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Gamepad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn reclassify_keyboard_on_alphabetic_key_event() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + + // on alphabetic key event + classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic); + assert!(classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn dont_reclassify_keyboard_on_non_alphabetic_key_event() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + + // on number key event + classifier.process_key(DEVICE_ID, KEY_1, ModifierState::None); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + #[test] + fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() { + let mut classifier = KeyboardClassifier::new(); + classifier.notify_keyboard_changed(create_device( + DeviceClass::Keyboard + | DeviceClass::Dpad + | DeviceClass::AlphabeticKey + | DeviceClass::External, + )); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + + classifier.process_key(DEVICE_ID, KEY_A, ModifierState::CtrlOn); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic); + assert!(!classifier.is_finalized(DEVICE_ID)); + } + + fn create_device(classes: DeviceClass) -> InputDevice { + InputDevice { + device_id: DEVICE_ID, + identifier: RustInputDeviceIdentifier { + name: "test_device".to_string(), + location: "location".to_string(), + unique_id: "unique_id".to_string(), + bus: 123, + vendor: 234, + product: 345, + version: 567, + descriptor: "descriptor".to_string(), + }, + classes, + } + } +} diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index fb3f520c01..5010475334 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -18,9 +18,13 @@ mod input; mod input_verifier; +mod keyboard_classifier; -pub use input::{DeviceId, MotionAction, MotionFlags, Source}; +pub use input::{ + DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source, +}; pub use input_verifier::InputVerifier; +pub use keyboard_classifier::KeyboardClassifier; #[cxx::bridge(namespace = "android::input")] #[allow(unsafe_op_in_unsafe_fn)] @@ -47,7 +51,8 @@ mod ffi { /// } /// ``` type InputVerifier; - fn create(name: String) -> Box<InputVerifier>; + #[cxx_name = create] + fn create_input_verifier(name: String) -> Box<InputVerifier>; fn process_movement( verifier: &mut InputVerifier, device_id: i32, @@ -59,15 +64,53 @@ mod ffi { fn reset_device(verifier: &mut InputVerifier, device_id: i32); } + #[namespace = "android::input::keyboardClassifier"] + extern "Rust" { + /// Used to classify a keyboard into alphabetic and non-alphabetic + type KeyboardClassifier; + #[cxx_name = create] + fn create_keyboard_classifier() -> Box<KeyboardClassifier>; + #[cxx_name = notifyKeyboardChanged] + fn notify_keyboard_changed( + classifier: &mut KeyboardClassifier, + device_id: i32, + identifier: RustInputDeviceIdentifier, + device_classes: u32, + ); + #[cxx_name = getKeyboardType] + fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32; + #[cxx_name = isFinalized] + fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool; + #[cxx_name = processKey] + fn process_key( + classifier: &mut KeyboardClassifier, + device_id: i32, + evdev_code: i32, + modifier_state: u32, + ); + } + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct RustPointerProperties { pub id: i32, } + + #[derive(Debug)] + pub struct RustInputDeviceIdentifier { + pub name: String, + pub location: String, + pub unique_id: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + pub version: u16, + pub descriptor: String, + } } -use crate::ffi::RustPointerProperties; +use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties}; -fn create(name: String) -> Box<InputVerifier> { +fn create_input_verifier(name: String) -> Box<InputVerifier> { Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"))) } @@ -103,3 +146,53 @@ fn process_movement( fn reset_device(verifier: &mut InputVerifier, device_id: i32) { verifier.reset_device(DeviceId(device_id)); } + +fn create_keyboard_classifier() -> Box<KeyboardClassifier> { + Box::new(KeyboardClassifier::new()) +} + +fn notify_keyboard_changed( + classifier: &mut KeyboardClassifier, + device_id: i32, + identifier: RustInputDeviceIdentifier, + device_classes: u32, +) { + let classes = DeviceClass::from_bits(device_classes); + if classes.is_none() { + panic!( + "The conversion of device class 0x{:08x} failed, please check if some device classes + have not been added to DeviceClass.", + device_classes + ); + } + classifier.notify_keyboard_changed(InputDevice { + device_id: DeviceId(device_id), + identifier, + classes: classes.unwrap(), + }); +} + +fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32 { + classifier.get_keyboard_type(DeviceId(device_id)) as u32 +} + +fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool { + classifier.is_finalized(DeviceId(device_id)) +} + +fn process_key( + classifier: &mut KeyboardClassifier, + device_id: i32, + evdev_code: i32, + meta_state: u32, +) { + let modifier_state = ModifierState::from_bits(meta_state); + if modifier_state.is_none() { + panic!( + "The conversion of meta state 0x{:08x} failed, please check if some meta state + have not been added to ModifierState.", + meta_state + ); + } + classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap()); +} diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 476b5cf818..3717f49fef 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,23 +26,39 @@ namespace android { +namespace { + // Default display id. -static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; +constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; -static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; +constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; -static constexpr auto POINTER_0_DOWN = +constexpr auto POINTER_0_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_1_DOWN = +constexpr auto POINTER_1_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_0_UP = +constexpr auto POINTER_0_UP = AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_1_UP = +constexpr auto POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +std::array<float, 9> asFloat9(const ui::Transform& t) { + std::array<float, 9> mat{}; + mat[0] = t[0][0]; + mat[1] = t[1][0]; + mat[2] = t[2][0]; + mat[3] = t[0][1]; + mat[4] = t[1][1]; + mat[5] = t[2][1]; + mat[6] = t[0][2]; + mat[7] = t[1][2]; + mat[8] = t[2][2]; + return mat; +} + class BaseTest : public testing::Test { protected: static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -50,6 +66,8 @@ protected: 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; }; +} // namespace + // --- PointerCoordsTest --- class PointerCoordsTest : public BaseTest { @@ -344,13 +362,15 @@ void MotionEventTest::SetUp() { } void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, - AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, mTransform, 2.0f, 2.1f, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, - mPointerProperties, mSamples[0].pointerCoords); + AMOTION_EVENT_ACTION_MOVE, 0, flags, AMOTION_EVENT_EDGE_FLAG_TOP, + AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, + mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME, + ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords); event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords); event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords); } @@ -364,7 +384,10 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); - ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); @@ -799,8 +822,10 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; ui::Transform identityTransform; + const int32_t flags = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0, + INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, identityTransform, /*xPrecision=*/0, /*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2, @@ -1087,4 +1112,90 @@ TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) { ASSERT_EQ(EXPECTED.y, event.getYCursorPosition()); } +TEST_F(MotionEventTest, InvalidOrientationNotRotated) { + // This touch event does not have a value for AXIS_ORIENTATION, and the flags are implicitly + // set to 0. The transform is set to a 90-degree rotation. + MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .build(); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); +} + +TEST_F(MotionEventTest, ValidZeroOrientationRotated) { + // This touch events will implicitly have a value of 0 for its AXIS_ORIENTATION. + auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); + MotionEvent nonDirectionalEvent = builder.build(); + MotionEvent directionalEvent = + builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build(); + + // The angle is rotated by the initial transform, a 90-degree rotation. + ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), M_PI_2, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + ASSERT_NEAR(fabs(directionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + + nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), -M_PI_2, EPSILON); +} + +TEST_F(MotionEventTest, ValidNonZeroOrientationRotated) { + const float initial = 1.f; + auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER) + .x(4) + .y(4) + .axis(AMOTION_EVENT_AXIS_ORIENTATION, initial)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); + + MotionEvent nonDirectionalEvent = builder.build(); + MotionEvent directionalEvent = + builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build(); + + // The angle is rotated by the initial transform, a 90-degree rotation. + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial + M_PI_2, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + + nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 70529bbd39..f49469ccca 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -96,7 +96,9 @@ PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; if (action == AMOTION_EVENT_ACTION_CANCEL) { flags |= AMOTION_EVENT_FLAG_CANCELED; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 48512f7c6e..e65a919bd6 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -89,7 +89,9 @@ PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; if (action == AMOTION_EVENT_ACTION_CANCEL) { flags |= AMOTION_EVENT_FLAG_CANCELED; } diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index 2dc9fdb7f1..8d8b5300c1 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -297,10 +297,9 @@ TEST_F(TouchResamplingTest, EventIsResampledWithDifferentId) { } /** - * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with - * a resampled timestamp and should be marked as such. + * Stylus pointer coordinates are resampled. */ -TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) { +TEST_F(TouchResamplingTest, StylusEventIsResampled) { std::chrono::nanoseconds frameTime; std::vector<InputEventEntry> entries, expectedEntries; @@ -330,15 +329,91 @@ TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) { // id x y {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE}, {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE}, - // A resampled event is generated, but the stylus coordinates are not resampled. {25ms, - {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}}, + {{0, 35, 30, .toolType = ToolType::STYLUS, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE}, }; consumeInputEventEntries(expectedEntries, frameTime); } /** + * Mouse pointer coordinates are resampled. + */ +TEST_F(TouchResamplingTest, MouseEventIsResampled) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE}, + {25ms, + {{0, 35, 30, .toolType = ToolType::MOUSE, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +/** + * Motion events with palm tool type are not resampled. + */ +TEST_F(TouchResamplingTest, PalmEventIsNotResampled) { + std::chrono::nanoseconds frameTime; + std::vector<InputEventEntry> entries, expectedEntries; + + // Initial ACTION_DOWN should be separate, because the first consume event will only return + // InputEvent with a single action. + entries = { + // id x y + {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN}, + }; + publishInputEventEntries(entries); + frameTime = 5ms; + expectedEntries = { + // id x y + {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN}, + }; + consumeInputEventEntries(expectedEntries, frameTime); + + // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y + entries = { + // id x y + {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE}, + }; + publishInputEventEntries(entries); + frameTime = 35ms; + expectedEntries = { + // id x y + {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE}, + {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE}, + }; + consumeInputEventEntries(expectedEntries, frameTime); +} + +/** * Event should not be resampled when sample time is equal to event time. */ TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) { diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 757d935647..4a04467308 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -50,6 +50,7 @@ cc_defaults { "libshaders", "libtonemap", "libsurfaceflinger_common", + "libsurfaceflingerflags", ], local_include_dirs: ["include"], export_include_dirs: ["include"], diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 1c60563b25..bc3976d9f1 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -22,24 +22,49 @@ #include "skia/SkiaGLRenderEngine.h" #include "threaded/RenderEngineThreaded.h" +#include <com_android_graphics_surfaceflinger_flags.h> #include <cutils/properties.h> #include <log/log.h> +// TODO: b/341728634 - Clean up conditional compilation. +#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \ + COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE) +#define COMPILE_GRAPHITE_RENDERENGINE 1 +#else +#define COMPILE_GRAPHITE_RENDERENGINE 0 +#endif + namespace android { namespace renderengine { std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { threaded::CreateInstanceFactory createInstanceFactory; +// TODO: b/341728634 - Clean up conditional compilation. +#if COMPILE_GRAPHITE_RENDERENGINE + const RenderEngine::SkiaBackend actualSkiaBackend = args.skiaBackend; +#else + if (args.skiaBackend == RenderEngine::SkiaBackend::GRAPHITE) { + ALOGE("RenderEngine with Graphite Skia backend was requested, but Graphite was not " + "included in the build. Falling back to Ganesh (%s)", + args.graphicsApi == RenderEngine::GraphicsApi::GL ? "GL" : "Vulkan"); + } + const RenderEngine::SkiaBackend actualSkiaBackend = RenderEngine::SkiaBackend::GANESH; +#endif + ALOGD("%sRenderEngine with %s Backend (%s)", args.threaded == Threaded::YES ? "Threaded " : "", args.graphicsApi == GraphicsApi::GL ? "SkiaGL" : "SkiaVK", - args.skiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite"); + actualSkiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite"); - if (args.skiaBackend == SkiaBackend::GRAPHITE) { +// TODO: b/341728634 - Clean up conditional compilation. +#if COMPILE_GRAPHITE_RENDERENGINE + if (actualSkiaBackend == SkiaBackend::GRAPHITE) { createInstanceFactory = [args]() { return android::renderengine::skia::GraphiteVkRenderEngine::create(args); }; - } else { // GANESH + } else +#endif + { // GANESH if (args.graphicsApi == GraphicsApi::VK) { createInstanceFactory = [args]() { return android::renderengine::skia::GaneshVkRenderEngine::create(args); diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp index fc16c5626d..5e756b03ed 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -197,7 +197,9 @@ void VulkanInterface::onVkDeviceFault(void* callbackContext, const std::string& LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); }; -static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { +static skgpu::VulkanGetProc sGetProc = [](const char* proc_name, + VkInstance instance, + VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); } @@ -604,7 +606,7 @@ void VulkanInterface::teardown() { mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice. mQueueIndex = 0; mApiVersion = 0; - mGrExtensions = GrVkExtensions(); + mGrExtensions = skgpu::VulkanExtensions(); mGrGetProc = nullptr; mIsProtected = false; mIsRealtimePriority = false; diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h index af0489a470..f20b00251b 100644 --- a/libs/renderengine/skia/VulkanInterface.h +++ b/libs/renderengine/skia/VulkanInterface.h @@ -17,7 +17,8 @@ #pragma once #include <include/gpu/vk/GrVkBackendContext.h> -#include <include/gpu/vk/GrVkExtensions.h> +#include <include/gpu/vk/VulkanExtensions.h> +#include <include/gpu/vk/VulkanTypes.h> #include <vulkan/vulkan.h> @@ -85,12 +86,12 @@ private: VkQueue mQueue = VK_NULL_HANDLE; int mQueueIndex = 0; uint32_t mApiVersion = 0; - GrVkExtensions mGrExtensions; + skgpu::VulkanExtensions mGrExtensions; VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr; VkPhysicalDeviceFaultFeaturesEXT* mDeviceFaultFeatures = nullptr; - GrVkGetProc mGrGetProc = nullptr; + skgpu::VulkanGetProc mGrGetProc = nullptr; bool mIsProtected = false; bool mIsRealtimePriority = false; diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 0eea187407..0783714eb9 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -46,6 +46,7 @@ cc_test { "libshaders", "libtonemap", "libsurfaceflinger_common", + "libsurfaceflingerflags", ], header_libs: [ "libtonemap_headers", @@ -64,5 +65,6 @@ cc_test { "libui", "libutils", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 4dcaff9ec8..a8a98236e2 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -22,6 +22,7 @@ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wextra" +#include <com_android_graphics_surfaceflinger_flags.h> #include <cutils/properties.h> #include <gtest/gtest.h> #include <renderengine/ExternalTexture.h> @@ -41,6 +42,14 @@ #include "../skia/SkiaVkRenderEngine.h" #include "../threaded/RenderEngineThreaded.h" +// TODO: b/341728634 - Clean up conditional compilation. +#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \ + COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE) +#define COMPILE_GRAPHITE_RENDERENGINE 1 +#else +#define COMPILE_GRAPHITE_RENDERENGINE 0 +#endif + constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; constexpr int DEFAULT_DISPLAY_OFFSET = 64; @@ -152,6 +161,8 @@ public: } }; +// TODO: b/341728634 - Clean up conditional compilation. +#if COMPILE_GRAPHITE_RENDERENGINE class GraphiteVkRenderEngineFactory : public RenderEngineFactory { public: std::string name() override { return "GraphiteVkRenderEngineFactory"; } @@ -164,6 +175,7 @@ public: return renderengine::RenderEngine::SkiaBackend::GRAPHITE; } }; +#endif class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { public: @@ -1497,10 +1509,15 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 expectBufferColor(Rect(kGreyLevels, 1), generator, 2); } +// TODO: b/341728634 - Clean up conditional compilation. INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), - std::make_shared<GaneshVkRenderEngineFactory>(), - std::make_shared<GraphiteVkRenderEngineFactory>())); + std::make_shared<GaneshVkRenderEngineFactory>() +#if COMPILE_GRAPHITE_RENDERENGINE + , + std::make_shared<GraphiteVkRenderEngineFactory>() +#endif + )); TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { if (!GetParam()->apiSupported()) { diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig index c511f4a72f..cbf3055fd4 100644 --- a/libs/sensor/libsensor_flags.aconfig +++ b/libs/sensor/libsensor_flags.aconfig @@ -8,3 +8,10 @@ flag { bug: "322228259" is_fixed_read_only: true } + +flag { + name: "sensor_event_queue_report_sensor_usage_in_tracing" + namespace: "sensors" + description: "When this flag is enabled, sensor event queue will report sensor usage when system trace is enabled." + bug: "333132224" +}
\ No newline at end of file diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 02453ef7a0..7d3a2df550 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -30,10 +30,6 @@ namespace android { -namespace input_flags = com::android::input::flags; -static const bool HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS = - input_flags::hide_pointer_indicators_for_secure_windows(); - namespace { bool isFromMouse(const NotifyMotionArgs& args) { @@ -106,8 +102,31 @@ std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowIn // --- PointerChoreographer --- -PointerChoreographer::PointerChoreographer(InputListenerInterface& listener, +PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener, PointerChoreographerPolicyInterface& policy) + : PointerChoreographer( + inputListener, policy, + [](const sp<android::gui::WindowInfosListener>& listener) { + auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{}, + std::vector<android::gui::DisplayInfo>{}); +#if defined(__ANDROID__) + SurfaceComposerClient::getDefault()->addWindowInfosListener(listener, + &initialInfo); +#endif + return initialInfo.first; + }, + [](const sp<android::gui::WindowInfosListener>& listener) { +#if defined(__ANDROID__) + SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener); +#endif + }) { +} + +PointerChoreographer::PointerChoreographer( + android::InputListenerInterface& listener, + android::PointerChoreographerPolicyInterface& policy, + const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener, + const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener) : mTouchControllerConstructor([this]() { return mPolicy.createPointerController( PointerControllerInterface::ControllerType::TOUCH); @@ -117,7 +136,9 @@ PointerChoreographer::PointerChoreographer(InputListenerInterface& listener, mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT), mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID), mShowTouchesEnabled(false), - mStylusPointerIconEnabled(false) {} + mStylusPointerIconEnabled(false), + mRegisterListener(registerListener), + mUnregisterListener(unregisterListener) {} PointerChoreographer::~PointerChoreographer() { std::scoped_lock _l(mLock); @@ -125,6 +146,7 @@ PointerChoreographer::~PointerChoreographer() { return; } mWindowInfoListener->onPointerChoreographerDestroyed(); + mUnregisterListener(mWindowInfoListener); } void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { @@ -391,7 +413,7 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) } void PointerChoreographer::onControllerAddedOrRemovedLocked() { - if (!HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS) { + if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) { return; } bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() || @@ -399,18 +421,10 @@ void PointerChoreographer::onControllerAddedOrRemovedLocked() { if (requireListener && mWindowInfoListener == nullptr) { mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this); - auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{}, - std::vector<android::gui::DisplayInfo>{}); -#if defined(__ANDROID__) - SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener, - &initialInfo); -#endif - mWindowInfoListener->setInitialDisplayInfos(initialInfo.first); + mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener)); onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays()); } else if (!requireListener && mWindowInfoListener != nullptr) { -#if defined(__ANDROID__) - SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener); -#endif + mUnregisterListener(mWindowInfoListener); mWindowInfoListener = nullptr; } else if (requireListener && mWindowInfoListener != nullptr) { // controller may have been added to an existing privacy sensitive display, we need to diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index 11c5a0c6c7..d9b075f3ee 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -108,10 +108,6 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; - // Public because it's also used by tests to simulate the WindowInfosListener callback - void onPrivacySensitiveDisplaysChanged( - const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays); - void dump(std::string& dump) override; private: @@ -139,6 +135,8 @@ private: void onPrivacySensitiveDisplaysChangedLocked( const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) REQUIRES(mLock); + void onPrivacySensitiveDisplaysChanged( + const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays); /* This listener keeps tracks of visible privacy sensitive displays and updates the * choreographer if there are any changes. @@ -194,6 +192,20 @@ private: bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden; + +protected: + using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>( + const sp<android::gui::WindowInfosListener>&)>; + using WindowListenerUnregisterConsumer = + std::function<void(const sp<android::gui::WindowInfosListener>&)>; + explicit PointerChoreographer(InputListenerInterface& listener, + PointerChoreographerPolicyInterface&, + const WindowListenerRegisterConsumer& registerListener, + const WindowListenerUnregisterConsumer& unregisterListener); + +private: + const WindowListenerRegisterConsumer mRegisterListener; + const WindowListenerUnregisterConsumer mUnregisterListener; }; } // namespace android diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 29aa3c3066..1a0ec48525 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -56,7 +56,9 @@ filegroup { cc_defaults { name: "libinputdispatcher_defaults", - srcs: [":libinputdispatcher_sources"], + srcs: [ + ":libinputdispatcher_sources", + ], shared_libs: [ "libbase", "libbinder", @@ -78,6 +80,7 @@ cc_defaults { "libattestation", "libgui_window_info_static", "libperfetto_client_experimental", + "perfetto_winscope_extensions_zero", ], target: { android: { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index dae2b6119c..5ed5eb8e33 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -444,10 +444,12 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato newCoords.copyFrom(motionEntry.pointerCoords[i]); // First, apply the current pointer's transform to update the coordinates into // window space. - newCoords.transform(currTransform); + MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source, + motionEntry.flags, currTransform); // Next, apply the inverse transform of the normalized coordinates so the // current coordinates are transformed into the normalized coordinate space. - newCoords.transform(inverseTransform); + MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source, + motionEntry.flags, inverseTransform); } } @@ -1894,8 +1896,6 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry); }; postCommandLocked(std::move(command)); - // Poke user activity for keys not passed to user - pokeUserActivityLocked(*entry); return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; @@ -1912,8 +1912,12 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED : InputEventInjectionResult::FAILED); mReporter->reportDroppedKey(entry->id); - // Poke user activity for undispatched keys - pokeUserActivityLocked(*entry); + // Poke user activity for consumed keys, as it may have not been reported due to + // the focused window requesting user activity to be disabled + if (*dropReason == DropReason::POLICY && + mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { + pokeUserActivityLocked(*entry); + } return true; } @@ -3313,22 +3317,16 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) { return; } - // If the key code is unknown, we don't consider it user activity - if (keyEntry.keyCode == AKEYCODE_UNKNOWN) { - return; - } // Don't inhibit events that were intercepted or are not passed to // the apps, like system shortcuts if (windowDisablingUserActivityInfo != nullptr && - keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP && - keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) { + keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) { if (DEBUG_DISPATCH_CYCLE) { ALOGD("Not poking user activity: disabled by window '%s'.", windowDisablingUserActivityInfo->name.c_str()); } return; } - break; } default: { @@ -4885,10 +4883,11 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev mLock.lock(); - if (policyFlags & POLICY_FLAG_FILTERED) { - // The events from InputFilter impersonate real hardware devices. Check these - // events for consistency and print an error. An inconsistent event sent from - // InputFilter could cause a crash in the later stages of dispatching pipeline. + { + // Verify all injected streams, whether the injection is coming from apps or from + // input filter. Print an error if the stream becomes inconsistent with this event. + // An inconsistent injected event sent could cause a crash in the later stages of + // dispatching pipeline. auto [it, _] = mInputFilterVerifiersByDisplay.try_emplace(displayId, std::string("Injection on ") + @@ -5132,8 +5131,8 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( } for (uint32_t i = 0; i < entry.getPointerCount(); i++) { entry.pointerCoords[i] = - MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay, - entry.pointerCoords[i]); + MotionEvent::calculateTransformedCoords(entry.source, entry.flags, + transformToDisplay, entry.pointerCoords[i]); } } @@ -5528,6 +5527,10 @@ void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) { } mFocusedDisplayId = displayId; + // Only a window on the focused display can have Pointer Capture, so disable the active + // Pointer Capture session if there is one, since the focused display changed. + disablePointerCaptureForcedLocked(); + // Find new focused window and validate sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId); sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken); @@ -6388,9 +6391,8 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) { - const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry)); fallbackKeyEntry = - afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled); + afterKeyEventLockedInterruptable(connection, &dispatchEntry, handled); } } // End critical section: The -LockedInterruptable methods may have released the lock. @@ -6614,8 +6616,17 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec } std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable( - const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry, - const KeyEntry& keyEntry, bool handled) { + const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) { + // The dispatchEntry is currently valid, but it might point to a deleted object after we release + // the lock. For simplicity, make copies of the data of interest here and assume that + // 'dispatchEntry' is not valid after this section. + // Hold a strong reference to the EventEntry to ensure it's valid for the duration of this + // function, even if the DispatchEntry gets destroyed and releases its share of the ownership. + std::shared_ptr<const EventEntry> eventEntry = dispatchEntry->eventEntry; + const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget(); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(eventEntry)); + // To prevent misuse, ensure dispatchEntry is no longer valid. + dispatchEntry = nullptr; if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) { if (!handled) { // Report the key as unhandled, since the fallback was not handled. @@ -6632,7 +6643,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl connection->inputState.removeFallbackKey(originalKeyCode); } - if (handled || !dispatchEntry.hasForegroundTarget()) { + if (handled || !hasForegroundTarget) { // If the application handles the original key for which we previously // generated a fallback or if the window is not a foreground window, // then cancel the associated fallback key, if any. @@ -6920,17 +6931,17 @@ void InputDispatcher::onFocusChangedLocked( enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason); } - // If a window has pointer capture, then it must have focus. We need to ensure that this - // contract is upheld when pointer capture is being disabled due to a loss of window focus. - // If the window loses focus before it loses pointer capture, then the window can be in a state - // where it has pointer capture but not focus, violating the contract. Therefore we must - // dispatch the pointer capture event before the focus event. Since focus events are added to - // the front of the queue (above), we add the pointer capture event to the front of the queue - // after the focus events are added. This ensures the pointer capture event ends up at the - // front. - disablePointerCaptureForcedLocked(); - if (mFocusedDisplayId == changes.displayId) { + // If a window has pointer capture, then it must have focus and must be on the top-focused + // display. We need to ensure that this contract is upheld when pointer capture is being + // disabled due to a loss of window focus. If the window loses focus before it loses pointer + // capture, then the window can be in a state where it has pointer capture but not focus, + // violating the contract. Therefore we must dispatch the pointer capture event before the + // focus event. Since focus events are added to the front of the queue (above), we add the + // pointer capture event to the front of the queue after the focus events are added. This + // ensures the pointer capture event ends up at the front. + disablePointerCaptureForcedLocked(); + sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 6240e7fe72..e2fc7a0d4f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -687,8 +687,8 @@ private: std::map<ui::LogicalDisplayId /*displayId*/, InputVerifier> mVerifiersByDisplay; // Returns a fallback KeyEntry that should be sent to the connection, if required. std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( - const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry, - const KeyEntry& keyEntry, bool handled) REQUIRES(mLock); + const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, + bool handled) REQUIRES(mLock); // Find touched state and touched window by token. std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/> diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp index 2d7554c9c1..0b17507c2c 100644 --- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -123,7 +123,8 @@ void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( const auto& coords = motion->pointerCoords[i]; const auto coordsInWindow = - MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords); + MotionEvent::calculateTransformedCoords(motion->source, motion->flags, + args.transform, coords); auto bits = BitSet64(coords.bits); for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { const uint32_t axis = bits.clearFirstMarkedBit(); diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp index 9b9633a1bd..3d30ad6c7b 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -23,6 +23,8 @@ #include <android-base/logging.h> #include <binder/IServiceManager.h> #include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <private/android_filesystem_config.h> #include <utils/String16.h> @@ -229,7 +231,9 @@ void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event, } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted() : inputEvent->set_dispatcher_motion_event(); AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted); @@ -253,7 +257,9 @@ void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted() : inputEvent->set_dispatcher_key_event(); AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted); @@ -277,7 +283,9 @@ void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchEvent = isRedacted ? inputEvent->set_dispatcher_window_dispatch_event_redacted() : inputEvent->set_dispatcher_window_dispatch_event(); diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index b807b2714f..2daf195757 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -237,6 +237,12 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL); mHasMic = mClasses.test(InputDeviceClass::MIC); + // Update keyboard type + if (mClasses.test(InputDeviceClass::KEYBOARD)) { + mContext->getKeyboardClassifier().notifyKeyboardChanged(mId, mIdentifier, mClasses.get()); + mKeyboardType = mContext->getKeyboardClassifier().getKeyboardType(mId); + } + using Change = InputReaderConfiguration::Change; if (!changes.any() || !isIgnored()) { @@ -403,7 +409,7 @@ std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t cou mDropUntilNextSync = true; } else { for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) { - out += mapper.process(rawEvent); + out += mapper.process(*rawEvent); }); } --count; @@ -445,6 +451,7 @@ InputDeviceInfo InputDevice::getDeviceInfo() { mHasMic, getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID), {mShouldSmoothScroll}, isEnabled()); + outDeviceInfo.setKeyboardType(static_cast<int32_t>(mKeyboardType)); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); }); @@ -517,13 +524,9 @@ std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers( // Keyboard-like devices. uint32_t keyboardSource = 0; - int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; if (classes.test(InputDeviceClass::KEYBOARD)) { keyboardSource |= AINPUT_SOURCE_KEYBOARD; } - if (classes.test(InputDeviceClass::ALPHAKEY)) { - keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; - } if (classes.test(InputDeviceClass::DPAD)) { keyboardSource |= AINPUT_SOURCE_DPAD; } @@ -532,8 +535,8 @@ std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers( } if (keyboardSource != 0) { - mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig, - keyboardSource, keyboardType)); + mappers.push_back( + createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig, keyboardSource)); } // Cursor-like devices. @@ -730,6 +733,13 @@ std::optional<int32_t> InputDevice::getBatteryEventHubId() const { return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt; } +void InputDevice::setKeyboardType(KeyboardType keyboardType) { + if (mKeyboardType != keyboardType) { + mKeyboardType = keyboardType; + bumpGeneration(); + } +} + InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index b9523ef332..ab13ad489b 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -102,6 +102,7 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, mEventHub(eventHub), mPolicy(policy), mNextListener(listener), + mKeyboardClassifier(std::make_unique<KeyboardClassifier>()), mGlobalMetaState(AMETA_NONE), mLedMetaState(AMETA_NONE), mGeneration(1), @@ -1076,4 +1077,8 @@ int32_t InputReader::ContextImpl::getNextId() { return mIdGenerator.nextId(); } +KeyboardClassifier& InputReader::ContextImpl::getKeyboardClassifier() { + return *mReader->mKeyboardClassifier; +} + } // namespace android diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 27b9d23ff5..49ad8b5d69 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -418,7 +418,11 @@ void PeripheralController::configureLights() { } rawInfos.insert_or_assign(rawId, rawInfo.value()); // Check if this is a group LEDs for player ID - std::regex lightPattern("([a-z]+)([0-9]+)"); + // The name for the light has already been parsed and is the `function` + // value; for player ID lights the function is expected to be `player-#`. + // However, the Sony driver will use `sony#` instead on SIXAXIS + // gamepads. + std::regex lightPattern("(player|sony)-?([0-9]+)"); std::smatch results; if (std::regex_match(rawInfo->name, results, lightPattern)) { std::string commonName = results[1].str(); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 39d2f5baab..7cf584df78 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -85,64 +85,67 @@ std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info); /* * Input device classes. + * + * These classes are duplicated in rust side here: /frameworks/native/libs/input/rust/input.rs. + * If any new classes are added, we need to add them in rust input side too. */ enum class InputDeviceClass : uint32_t { /* The input device is a keyboard or has buttons. */ - KEYBOARD = 0x00000001, + KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ - ALPHAKEY = 0x00000002, + ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY, /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ - TOUCH = 0x00000004, + TOUCH = android::os::IInputConstants::DEVICE_CLASS_TOUCH, /* The input device is a cursor device such as a trackball or mouse. */ - CURSOR = 0x00000008, + CURSOR = android::os::IInputConstants::DEVICE_CLASS_CURSOR, /* The input device is a multi-touch touchscreen or touchpad. */ - TOUCH_MT = 0x00000010, + TOUCH_MT = android::os::IInputConstants::DEVICE_CLASS_TOUCH_MT, /* The input device is a directional pad (implies keyboard, has DPAD keys). */ - DPAD = 0x00000020, + DPAD = android::os::IInputConstants::DEVICE_CLASS_DPAD, /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ - GAMEPAD = 0x00000040, + GAMEPAD = android::os::IInputConstants::DEVICE_CLASS_GAMEPAD, /* The input device has switches. */ - SWITCH = 0x00000080, + SWITCH = android::os::IInputConstants::DEVICE_CLASS_SWITCH, /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ - JOYSTICK = 0x00000100, + JOYSTICK = android::os::IInputConstants::DEVICE_CLASS_JOYSTICK, /* The input device has a vibrator (supports FF_RUMBLE). */ - VIBRATOR = 0x00000200, + VIBRATOR = android::os::IInputConstants::DEVICE_CLASS_VIBRATOR, /* The input device has a microphone. */ - MIC = 0x00000400, + MIC = android::os::IInputConstants::DEVICE_CLASS_MIC, /* The input device is an external stylus (has data we want to fuse with touch data). */ - EXTERNAL_STYLUS = 0x00000800, + EXTERNAL_STYLUS = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS, /* The input device has a rotary encoder */ - ROTARY_ENCODER = 0x00001000, + ROTARY_ENCODER = android::os::IInputConstants::DEVICE_CLASS_ROTARY_ENCODER, /* The input device has a sensor like accelerometer, gyro, etc */ - SENSOR = 0x00002000, + SENSOR = android::os::IInputConstants::DEVICE_CLASS_SENSOR, /* The input device has a battery */ - BATTERY = 0x00004000, + BATTERY = android::os::IInputConstants::DEVICE_CLASS_BATTERY, /* The input device has sysfs controllable lights */ - LIGHT = 0x00008000, + LIGHT = android::os::IInputConstants::DEVICE_CLASS_LIGHT, /* The input device is a touchpad, requiring an on-screen cursor. */ - TOUCHPAD = 0x00010000, + TOUCHPAD = android::os::IInputConstants::DEVICE_CLASS_TOUCHPAD, /* The input device is virtual (not a real device, not part of UI configuration). */ - VIRTUAL = 0x40000000, + VIRTUAL = android::os::IInputConstants::DEVICE_CLASS_VIRTUAL, /* The input device is external (not built-in). */ - EXTERNAL = 0x80000000, + EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL, }; enum class SysfsClass : uint32_t { diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 4c9af2e30e..2a7e262bf5 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -79,6 +79,8 @@ public: inline bool isIgnored() { return !getMapperCount() && !mController; } + inline KeyboardType getKeyboardType() const { return mKeyboardType; } + bool isEnabled(); void dump(std::string& dump, const std::string& eventHubDevStr); @@ -124,6 +126,8 @@ public: void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + void setKeyboardType(KeyboardType keyboardType); + void bumpGeneration(); [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when); @@ -196,6 +200,7 @@ private: uint32_t mSources; bool mIsWaking; bool mIsExternal; + KeyboardType mKeyboardType = KeyboardType::NONE; std::optional<uint8_t> mAssociatedDisplayPort; std::optional<std::string> mAssociatedDisplayUniqueIdByPort; std::optional<std::string> mAssociatedDisplayUniqueIdByDescriptor; @@ -470,6 +475,10 @@ public: } inline void bumpGeneration() { mDevice.bumpGeneration(); } inline const PropertyMap& getConfiguration() const { return mDevice.getConfiguration(); } + inline KeyboardType getKeyboardType() const { return mDevice.getKeyboardType(); } + inline void setKeyboardType(KeyboardType keyboardType) { + return mDevice.setKeyboardType(keyboardType); + } private: InputDevice& mDevice; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 7e701c5341..6f8c289093 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -157,6 +157,7 @@ protected: void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock) REQUIRES(mLock) override; nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override; + KeyboardClassifier& getKeyboardClassifier() override; } mContext; friend class ContextImpl; @@ -176,6 +177,10 @@ private: // The next stage that should receive the events generated inside InputReader. InputListenerInterface& mNextListener; + + // Classifier for keyboard/keyboard-like devices + std::unique_ptr<KeyboardClassifier> mKeyboardClassifier; + // As various events are generated inside InputReader, they are stored inside this list. The // list can only be accessed with the lock, so the events inside it are well-ordered. // Once the reader is done working, these events will be swapped into a temporary storage and diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 907a49faa2..e0e0ac2051 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -17,6 +17,7 @@ #pragma once #include <input/InputDevice.h> +#include <input/KeyboardClassifier.h> #include "NotifyArgs.h" #include <vector> @@ -64,6 +65,8 @@ public: virtual void setLastKeyDownTimestamp(nsecs_t when) = 0; virtual nsecs_t getLastKeyDownTimestamp() = 0; + + virtual KeyboardClassifier& getKeyboardClassifier() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp index 09a5f08a23..90685dec2b 100644 --- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp +++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp @@ -155,8 +155,8 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::process(const RawEvent& ra mMotionAccumulator.finishSync(); } - mCursorButtonAccumulator.process(&rawEvent); - mMotionAccumulator.process(&rawEvent); + mCursorButtonAccumulator.process(rawEvent); + mMotionAccumulator.process(rawEvent); return out; } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index c67314dbcf..20cdb59b00 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -55,14 +55,14 @@ void CursorMotionAccumulator::clearRelativeAxes() { mRelY = 0; } -void CursorMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { +void CursorMotionAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_REL) { + switch (rawEvent.code) { case REL_X: - mRelX = rawEvent->value; + mRelX = rawEvent.value; break; case REL_Y: - mRelY = rawEvent->value; + mRelY = rawEvent.value; break; } } @@ -215,16 +215,16 @@ std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) { return InputMapper::reset(when); } -std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> CursorInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; mCursorButtonAccumulator.process(rawEvent); mCursorMotionAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { const auto [eventTime, readTime] = applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), - rawEvent->when, rawEvent->readTime, + rawEvent.when, rawEvent.readTime, mLastEventTime); out += sync(eventTime, readTime); mLastEventTime = eventTime; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 75ca9c00a8..2108488936 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -34,7 +34,7 @@ public: CursorMotionAccumulator(); void reset(InputDeviceContext& deviceContext); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); void finishSync(); inline int32_t getRelativeX() const { return mRelX; } @@ -62,7 +62,7 @@ public: const InputReaderConfiguration& readerConfig, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 987d2d0221..3af1d04073 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -61,13 +61,13 @@ std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) { return InputMapper::reset(when); } -std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; mSingleTouchMotionAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - out += sync(rawEvent->when); + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + out += sync(rawEvent.when); } return out; } diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 97df02b69f..c040a7b996 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -39,7 +39,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index c7eea0e980..2c51448b4e 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -78,7 +78,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes); [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when); - [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0; + [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent& rawEvent) = 0; [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when); virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 5ce4d30bb3..41e018d392 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -259,29 +259,29 @@ std::list<NotifyArgs> JoystickInputMapper::reset(nsecs_t when) { return InputMapper::reset(when); } -std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; - switch (rawEvent->type) { + switch (rawEvent.type) { case EV_ABS: { - auto it = mAxes.find(rawEvent->code); + auto it = mAxes.find(rawEvent.code); if (it != mAxes.end()) { Axis& axis = it->second; float newValue, highNewValue; switch (axis.axisInfo.mode) { case AxisInfo::MODE_INVERT: - newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + + newValue = (axis.rawAxisInfo.maxValue - rawEvent.value) * axis.scale + axis.offset; highNewValue = 0.0f; break; case AxisInfo::MODE_SPLIT: - if (rawEvent->value < axis.axisInfo.splitValue) { - newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + + if (rawEvent.value < axis.axisInfo.splitValue) { + newValue = (axis.axisInfo.splitValue - rawEvent.value) * axis.scale + axis.offset; highNewValue = 0.0f; - } else if (rawEvent->value > axis.axisInfo.splitValue) { + } else if (rawEvent.value > axis.axisInfo.splitValue) { newValue = 0.0f; highNewValue = - (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + + (rawEvent.value - axis.axisInfo.splitValue) * axis.highScale + axis.highOffset; } else { newValue = 0.0f; @@ -289,7 +289,7 @@ std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) { } break; default: - newValue = rawEvent->value * axis.scale + axis.offset; + newValue = rawEvent.value * axis.scale + axis.offset; highNewValue = 0.0f; break; } @@ -300,9 +300,9 @@ std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) { } case EV_SYN: - switch (rawEvent->code) { + switch (rawEvent.code) { case SYN_REPORT: - out += sync(rawEvent->when, rawEvent->readTime, /*force=*/false); + out += sync(rawEvent.when, rawEvent.readTime, /*force=*/false); break; } break; diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index 313f0922b7..621d38bfaa 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -35,7 +35,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; private: struct Axis { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 21245555a2..91ec62d3d4 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -21,6 +21,7 @@ #include "KeyboardInputMapper.h" #include <ftl/enum.h> +#include <input/KeyboardClassifier.h> #include <ui/Rotation.h> namespace android { @@ -96,8 +97,8 @@ static bool isMediaKey(int32_t keyCode) { KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig, - uint32_t source, int32_t keyboardType) - : InputMapper(deviceContext, readerConfig), mSource(source), mKeyboardType(keyboardType) {} + uint32_t source) + : InputMapper(deviceContext, readerConfig), mSource(source) {} uint32_t KeyboardInputMapper::getSources() const { return mSource; @@ -131,7 +132,6 @@ std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() c void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) { InputMapper::populateDeviceInfo(info); - info.setKeyboardType(mKeyboardType); info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap()); std::optional keyboardLayoutInfo = getKeyboardLayoutInfo(); @@ -143,7 +143,6 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) { void KeyboardInputMapper::dump(std::string& dump) { dump += INDENT2 "Keyboard Input Mapper:\n"; dumpParameters(dump); - dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(getOrientation()).c_str()); dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); @@ -237,15 +236,15 @@ std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) { return out; } -std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; - mHidUsageAccumulator.process(*rawEvent); - switch (rawEvent->type) { + mHidUsageAccumulator.process(rawEvent); + switch (rawEvent.type) { case EV_KEY: { - int32_t scanCode = rawEvent->code; + int32_t scanCode = rawEvent.code; if (isSupportedScanCode(scanCode)) { - out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, + out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0, scanCode, mHidUsageAccumulator.consumeCurrentHidUsage()); } break; @@ -327,13 +326,24 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read keyMetaState = mMetaState; } + DeviceId deviceId = getDeviceId(); + + // On first down: Process key for keyboard classification (will send reconfiguration if the + // keyboard type change) + if (down && !keyDownIndex) { + KeyboardClassifier& classifier = getDeviceContext().getContext()->getKeyboardClassifier(); + classifier.processKey(deviceId, scanCode, keyMetaState); + getDeviceContext().setKeyboardType(classifier.getKeyboardType(deviceId)); + } + + KeyboardType keyboardType = getDeviceContext().getKeyboardType(); // Any key down on an external keyboard should wake the device. // We don't do this for internal keyboards to prevent them from waking up in your pocket. // For internal keyboards and devices for which the default wake behavior is explicitly // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each // wake key individually. if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault && - !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) { + !(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) { policyFlags |= POLICY_FLAG_WAKE; } @@ -341,8 +351,8 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, getDisplayId(), policyFlags, + out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, deviceId, mSource, + getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags, keyCode, scanCode, keyMetaState, downTime)); return out; diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index f2d3f4d7d8..c7df558caf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -36,7 +36,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; @@ -61,7 +61,6 @@ private: }; uint32_t mSource{}; - int32_t mKeyboardType{}; std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo; std::vector<KeyDown> mKeyDowns{}; // keys that are down @@ -85,8 +84,7 @@ private: } mParameters{}; KeyboardInputMapper(InputDeviceContext& deviceContext, - const InputReaderConfiguration& readerConfig, uint32_t source, - int32_t keyboardType); + const InputReaderConfiguration& readerConfig, uint32_t source); void configureParameters(); void dumpParameters(std::string& dump) const; diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index bca9d9183b..1986fe286a 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -40,7 +40,7 @@ std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) { return TouchInputMapper::reset(when); } -std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent); mMultiTouchMotionAccumulator.process(rawEvent); diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 5c173f365e..cca23279c5 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -31,7 +31,7 @@ public: ~MultiTouchInputMapper() override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when, const InputReaderConfiguration& config, ConfigurationChanges changes) override; diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 0ddbc06890..27ff52fa65 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -101,12 +101,12 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) { return InputMapper::reset(when); } -std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; mRotaryEncoderScrollAccumulator.process(rawEvent); - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - out += sync(rawEvent->when, rawEvent->readTime); + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + out += sync(rawEvent.when, rawEvent.readTime); } return out; } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index fe5d152cef..14c540bf6e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -39,7 +39,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; private: CursorScrollAccumulator mRotaryEncoderScrollAccumulator; diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index a131e3598f..d7f2993daa 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -251,35 +251,35 @@ void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime mPrevMscTime = static_cast<uint32_t>(mscTime); } -std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> SensorInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; - switch (rawEvent->type) { + switch (rawEvent.type) { case EV_ABS: { - auto it = mAxes.find(rawEvent->code); + auto it = mAxes.find(rawEvent.code); if (it != mAxes.end()) { Axis& axis = it->second; - axis.newValue = rawEvent->value * axis.scale + axis.offset; + axis.newValue = rawEvent.value * axis.scale + axis.offset; } break; } case EV_SYN: - switch (rawEvent->code) { + switch (rawEvent.code) { case SYN_REPORT: for (std::pair<const int32_t, Axis>& pair : mAxes) { Axis& axis = pair.second; axis.currentValue = axis.newValue; } - out += sync(rawEvent->when, /*force=*/false); + out += sync(rawEvent.when, /*force=*/false); break; } break; case EV_MSC: - switch (rawEvent->code) { + switch (rawEvent.code) { case MSC_TIMESTAMP: // hardware timestamp is nano seconds - processHardWareTimestamp(rawEvent->when, rawEvent->value); + processHardWareTimestamp(rawEvent.when, rawEvent.value); break; } } diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h index a55dcd1905..63bc151ac1 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.h +++ b/services/inputflinger/reader/mapper/SensorInputMapper.h @@ -40,7 +40,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, std::chrono::microseconds maxBatchReportLatency) override; void disableSensor(InputDeviceSensorType sensorType) override; diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp index ed0e27067f..140bb0c4ed 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -30,7 +30,7 @@ std::list<NotifyArgs> SingleTouchInputMapper::reset(nsecs_t when) { return TouchInputMapper::reset(when); } -std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent); mSingleTouchMotionAccumulator.process(rawEvent); diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h index 7726bfb159..bc38711c98 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -31,7 +31,7 @@ public: ~SingleTouchInputMapper() override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; protected: void syncTouch(nsecs_t when, RawState* outState) override; diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 05338da146..f131fb73df 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -30,16 +30,16 @@ uint32_t SwitchInputMapper::getSources() const { return AINPUT_SOURCE_SWITCH; } -std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> out; - switch (rawEvent->type) { + switch (rawEvent.type) { case EV_SW: - processSwitch(rawEvent->code, rawEvent->value); + processSwitch(rawEvent.code, rawEvent.value); break; case EV_SYN: - if (rawEvent->code == SYN_REPORT) { - out += sync(rawEvent->when); + if (rawEvent.code == SYN_REPORT) { + out += sync(rawEvent.when); } } return out; diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index 2fb48bbf25..5d8aa2c784 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -29,7 +29,7 @@ public: virtual ~SwitchInputMapper(); virtual uint32_t getSources() const override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override; virtual void dump(std::string& dump) override; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 8b4b691a45..a3834908a7 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -537,18 +537,6 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { return getDeviceContext().getAssociatedViewport(); } - const std::optional<std::string> associatedDisplayUniqueIdByDescriptor = - getDeviceContext().getAssociatedDisplayUniqueIdByDescriptor(); - if (associatedDisplayUniqueIdByDescriptor) { - return getDeviceContext().getAssociatedViewport(); - } - - const std::optional<std::string> associatedDisplayUniqueIdByPort = - getDeviceContext().getAssociatedDisplayUniqueIdByPort(); - if (associatedDisplayUniqueIdByPort) { - return getDeviceContext().getAssociatedViewport(); - } - if (mDeviceMode == DeviceMode::POINTER) { std::optional<DisplayViewport> viewport = mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); @@ -1415,14 +1403,14 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } -std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> TouchInputMapper::process(const RawEvent& rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); std::list<NotifyArgs> out; - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - out += sync(rawEvent->when, rawEvent->readTime); + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + out += sync(rawEvent.when, rawEvent.readTime); } return out; } @@ -2355,20 +2343,23 @@ void TouchInputMapper::cookPointerData() { if (mHaveTilt) { float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle))); + orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)), + /*isDirectional=*/true); tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); } else { tilt = 0; switch (mCalibration.orientationCalibration) { case Calibration::OrientationCalibration::INTERPOLATED: - orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale); + orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale, + /*isDirectional=*/true); break; case Calibration::OrientationCalibration::VECTOR: { int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); int32_t c2 = signExtendNybble(in.orientation & 0x0f); if (c1 != 0 || c2 != 0) { - orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f); + orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f, + /*isDirectional=*/true); float confidence = hypotf(c1, c2); float scale = 1.0f + confidence / 16.0f; touchMajor *= scale; @@ -3684,6 +3675,14 @@ NotifyMotionArgs TouchInputMapper::dispatchMotion( if (mCurrentStreamModifiedByExternalStylus) { source |= AINPUT_SOURCE_BLUETOOTH_STYLUS; } + if (mOrientedRanges.orientation.has_value()) { + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION; + if (mOrientedRanges.tilt.has_value()) { + // In the current implementation, only devices that report a value for tilt supports + // directional orientation. + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; + } + } const ui::LogicalDisplayId displayId = getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index b24f2ff8da..30c58a59c5 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -174,7 +174,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index b8911db2da..24efae893e 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -24,6 +24,7 @@ #include <mutex> #include <optional> +#include <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <android/input.h> @@ -241,10 +242,10 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) { RawAbsoluteAxisInfo slotAxisInfo; deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo); - if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) { - ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work " - "properly.", - deviceContext.getName().c_str()); + if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) { + LOG(WARNING) << "Touchpad " << deviceContext.getName() + << " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly."; + slotAxisInfo.maxValue = 0; } mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true); @@ -416,17 +417,17 @@ void TouchpadInputMapper::resetGestureInterpreter(nsecs_t when) { mResettingInterpreter = false; } -std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent& rawEvent) { if (mPointerCaptured) { - return mCapturedEventConverter.process(*rawEvent); + return mCapturedEventConverter.process(rawEvent); } if (mMotionAccumulator.getActiveSlotsCount() == 0) { - mGestureStartTime = rawEvent->when; + mGestureStartTime = rawEvent.when; } std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent); if (state) { updatePalmDetectionMetrics(); - return sendHardwareState(rawEvent->when, rawEvent->readTime, *state); + return sendHardwareState(rawEvent.when, rawEvent.readTime, *state); } else { return {}; } diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index 546fa5bb9c..8baa63e8e0 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -56,7 +56,7 @@ public: const InputReaderConfiguration& config, ConfigurationChanges changes) override; [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override; void consumeGesture(const Gesture* gesture); diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 8d78d0fd80..a3a48ef034 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -36,7 +36,7 @@ void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo& info) { info.setVibrator(true); } -std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) { +std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent& rawEvent) { // TODO: Handle FF_STATUS, although it does not seem to be widely supported. return {}; } diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index 9079c73f8e..75196821af 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -30,7 +30,7 @@ public: virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override; - [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override; [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override; diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp index 153236c177..9e722d41e7 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp @@ -47,32 +47,32 @@ void CursorButtonAccumulator::clearButtons() { mBtnTask = 0; } -void CursorButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { +void CursorButtonAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_KEY) { + switch (rawEvent.code) { case BTN_LEFT: - mBtnLeft = rawEvent->value; + mBtnLeft = rawEvent.value; break; case BTN_RIGHT: - mBtnRight = rawEvent->value; + mBtnRight = rawEvent.value; break; case BTN_MIDDLE: - mBtnMiddle = rawEvent->value; + mBtnMiddle = rawEvent.value; break; case BTN_BACK: - mBtnBack = rawEvent->value; + mBtnBack = rawEvent.value; break; case BTN_SIDE: - mBtnSide = rawEvent->value; + mBtnSide = rawEvent.value; break; case BTN_FORWARD: - mBtnForward = rawEvent->value; + mBtnForward = rawEvent.value; break; case BTN_EXTRA: - mBtnExtra = rawEvent->value; + mBtnExtra = rawEvent.value; break; case BTN_TASK: - mBtnTask = rawEvent->value; + mBtnTask = rawEvent.value; break; } } diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h index 6960644a1d..256b2bb994 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h @@ -29,7 +29,7 @@ public: CursorButtonAccumulator(); void reset(const InputDeviceContext& deviceContext); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); uint32_t getButtonState() const; inline bool isLeftPressed() const { return mBtnLeft; } diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp index 07146941fd..f85cab205b 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp @@ -39,14 +39,14 @@ void CursorScrollAccumulator::clearRelativeAxes() { mRelHWheel = 0; } -void CursorScrollAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { +void CursorScrollAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_REL) { + switch (rawEvent.code) { case REL_WHEEL: - mRelWheel = rawEvent->value; + mRelWheel = rawEvent.value; break; case REL_HWHEEL: - mRelHWheel = rawEvent->value; + mRelHWheel = rawEvent.value; break; } } diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h index ae1b7a32f4..e563620252 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -31,7 +31,7 @@ public: void configure(InputDeviceContext& deviceContext); void reset(InputDeviceContext& deviceContext); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); void finishSync(); inline bool haveRelativeVWheel() const { return mHaveRelWheel; } diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index b3f170075c..4919068201 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -45,12 +45,12 @@ void MultiTouchMotionAccumulator::resetSlots() { mCurrentSlot = -1; } -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { +void MultiTouchMotionAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_ABS) { bool newSlot = false; if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; + if (rawEvent.code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent.value; newSlot = true; } } else if (mCurrentSlot < 0) { @@ -72,12 +72,12 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { if (!mUsingSlotsProtocol) { slot.mInUse = true; } - if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) { - warnIfNotInUse(*rawEvent, slot); + if (rawEvent.code == ABS_MT_POSITION_X || rawEvent.code == ABS_MT_POSITION_Y) { + warnIfNotInUse(rawEvent, slot); } - slot.populateAxisValue(rawEvent->code, rawEvent->value); + slot.populateAxisValue(rawEvent.code, rawEvent.value); } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + } else if (rawEvent.type == EV_SYN && rawEvent.code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; } diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index a0f21470c4..388ed82373 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -76,7 +76,7 @@ public: void configure(const InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); void reset(const InputDeviceContext& deviceContext); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); void finishSync(); size_t getActiveSlotsCount() const; diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp index 27b8e40fc6..2b82ddf33d 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp @@ -45,29 +45,29 @@ void SingleTouchMotionAccumulator::clearAbsoluteAxes() { mAbsTiltY = 0; } -void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - switch (rawEvent->code) { +void SingleTouchMotionAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_ABS) { + switch (rawEvent.code) { case ABS_X: - mAbsX = rawEvent->value; + mAbsX = rawEvent.value; break; case ABS_Y: - mAbsY = rawEvent->value; + mAbsY = rawEvent.value; break; case ABS_PRESSURE: - mAbsPressure = rawEvent->value; + mAbsPressure = rawEvent.value; break; case ABS_TOOL_WIDTH: - mAbsToolWidth = rawEvent->value; + mAbsToolWidth = rawEvent.value; break; case ABS_DISTANCE: - mAbsDistance = rawEvent->value; + mAbsDistance = rawEvent.value; break; case ABS_TILT_X: - mAbsTiltX = rawEvent->value; + mAbsTiltX = rawEvent.value; break; case ABS_TILT_Y: - mAbsTiltY = rawEvent->value; + mAbsTiltY = rawEvent.value; break; } } diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h index 93056f06e6..fb74bcaf4c 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h @@ -28,7 +28,7 @@ class SingleTouchMotionAccumulator { public: SingleTouchMotionAccumulator(); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); void reset(InputDeviceContext& deviceContext); inline int32_t getAbsoluteX() const { return mAbsX; } diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index 8c4bed3267..ba8577e462 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -52,60 +52,60 @@ void TouchButtonAccumulator::reset() { mHidUsageAccumulator.reset(); } -void TouchButtonAccumulator::process(const RawEvent* rawEvent) { - mHidUsageAccumulator.process(*rawEvent); +void TouchButtonAccumulator::process(const RawEvent& rawEvent) { + mHidUsageAccumulator.process(rawEvent); - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { + if (rawEvent.type == EV_KEY) { + switch (rawEvent.code) { case BTN_TOUCH: - mBtnTouch = rawEvent->value; + mBtnTouch = rawEvent.value; break; case BTN_STYLUS: - mBtnStylus = rawEvent->value; + mBtnStylus = rawEvent.value; break; case BTN_STYLUS2: case BTN_0: // BTN_0 is what gets mapped for the HID usage // Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = rawEvent->value; + mBtnStylus2 = rawEvent.value; break; case BTN_TOOL_FINGER: - mBtnToolFinger = rawEvent->value; + mBtnToolFinger = rawEvent.value; break; case BTN_TOOL_PEN: - mBtnToolPen = rawEvent->value; + mBtnToolPen = rawEvent.value; break; case BTN_TOOL_RUBBER: - mBtnToolRubber = rawEvent->value; + mBtnToolRubber = rawEvent.value; break; case BTN_TOOL_BRUSH: - mBtnToolBrush = rawEvent->value; + mBtnToolBrush = rawEvent.value; break; case BTN_TOOL_PENCIL: - mBtnToolPencil = rawEvent->value; + mBtnToolPencil = rawEvent.value; break; case BTN_TOOL_AIRBRUSH: - mBtnToolAirbrush = rawEvent->value; + mBtnToolAirbrush = rawEvent.value; break; case BTN_TOOL_MOUSE: - mBtnToolMouse = rawEvent->value; + mBtnToolMouse = rawEvent.value; break; case BTN_TOOL_LENS: - mBtnToolLens = rawEvent->value; + mBtnToolLens = rawEvent.value; break; case BTN_TOOL_DOUBLETAP: - mBtnToolDoubleTap = rawEvent->value; + mBtnToolDoubleTap = rawEvent.value; break; case BTN_TOOL_TRIPLETAP: - mBtnToolTripleTap = rawEvent->value; + mBtnToolTripleTap = rawEvent.value; break; case BTN_TOOL_QUADTAP: - mBtnToolQuadTap = rawEvent->value; + mBtnToolQuadTap = rawEvent.value; break; case BTN_TOOL_QUINTTAP: - mBtnToolQuintTap = rawEvent->value; + mBtnToolQuintTap = rawEvent.value; break; default: - processMappedKey(rawEvent->code, rawEvent->value); + processMappedKey(rawEvent.code, rawEvent.value); } return; } diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index e829692206..c7adf8448e 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -33,7 +33,7 @@ public: void configure(); void reset(); - void process(const RawEvent* rawEvent); + void process(const RawEvent& rawEvent); uint32_t getButtonState() const; ToolType getToolType() const; diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp index b89b7f38a9..6885adb242 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp @@ -40,15 +40,15 @@ HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceC } std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent( - const RawEvent* rawEvent) { + const RawEvent& rawEvent) { std::optional<SelfContainedHardwareState> out; - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - out = produceHardwareState(rawEvent->when); + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + out = produceHardwareState(rawEvent.when); mMotionAccumulator.finishSync(); mMscTimestamp = 0; } - if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { - mMscTimestamp = rawEvent->value; + if (rawEvent.type == EV_MSC && rawEvent.code == MSC_TIMESTAMP) { + mMscTimestamp = rawEvent.value; } mCursorButtonAccumulator.process(rawEvent); mMotionAccumulator.process(rawEvent); diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h index 633448e67e..07e62c6eba 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h @@ -44,7 +44,7 @@ public: HardwareStateConverter(const InputDeviceContext& deviceContext, MultiTouchMotionAccumulator& motionAccumulator); - std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event); + std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent& event); void reset(); private: diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp index 69264f84ed..f4a5e0dfeb 100644 --- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp +++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp @@ -90,7 +90,7 @@ void PropertyProvider::loadPropertiesFromIdcFile(const PropertyMap& idcPropertie // prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the // configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property. const std::string gesturePropPrefix = "gestureProp."; - for (const std::string key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) { + for (const std::string& key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) { std::string propertyName = key.substr(gesturePropPrefix.length()); for (size_t i = 0; i < propertyName.length(); i++) { if (propertyName[i] == '_') { diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp index 255c7eb679..5b7cc2d432 100644 --- a/services/inputflinger/rust/Android.bp +++ b/services/inputflinger/rust/Android.bp @@ -47,6 +47,7 @@ rust_defaults { "liblog_rust", "liblogger", "libnix", + "libinput_rust", ], host_supported: true, } diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 6df339ed67..8b44af38db 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -31,6 +31,7 @@ use crate::bounce_keys_filter::BounceKeysFilter; use crate::input_filter_thread::InputFilterThread; use crate::slow_keys_filter::SlowKeysFilter; use crate::sticky_keys_filter::StickyKeysFilter; +use input::ModifierState; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; @@ -169,12 +170,15 @@ impl ModifierStateListener { Self(callbacks) } - pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) { - let _ = self - .0 - .read() - .unwrap() - .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32); + pub fn modifier_state_changed( + &self, + modifier_state: ModifierState, + locked_modifier_state: ModifierState, + ) { + let _ = self.0.read().unwrap().onModifierStateChanged( + modifier_state.bits() as i32, + locked_modifier_state.bits() as i32, + ); } } @@ -396,14 +400,15 @@ pub mod test_callbacks { IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback}, KeyEvent::KeyEvent, }; + use input::ModifierState; use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId}; use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc, RwLock, RwLockWriteGuard}; use std::time::Duration; #[derive(Default)] struct TestCallbacksInner { - last_modifier_state: u32, - last_locked_modifier_state: u32, + last_modifier_state: ModifierState, + last_locked_modifier_state: ModifierState, last_event: Option<KeyEvent>, test_thread: Option<FakeCppThread>, } @@ -428,15 +433,15 @@ pub mod test_callbacks { pub fn clear(&mut self) { self.inner().last_event = None; - self.inner().last_modifier_state = 0; - self.inner().last_locked_modifier_state = 0; + self.inner().last_modifier_state = ModifierState::None; + self.inner().last_locked_modifier_state = ModifierState::None; } - pub fn get_last_modifier_state(&self) -> u32 { + pub fn get_last_modifier_state(&self) -> ModifierState { self.0.read().unwrap().last_modifier_state } - pub fn get_last_locked_modifier_state(&self) -> u32 { + pub fn get_last_locked_modifier_state(&self) -> ModifierState { self.0.read().unwrap().last_locked_modifier_state } @@ -459,8 +464,10 @@ pub mod test_callbacks { 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; + self.inner().last_modifier_state = + ModifierState::from_bits(modifier_state as u32).unwrap(); + self.inner().last_locked_modifier_state = + ModifierState::from_bits(locked_modifier_state as u32).unwrap(); Result::Ok(()) } diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs index 6c2277c813..6c7c7fba39 100644 --- a/services/inputflinger/rust/sticky_keys_filter.rs +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -23,6 +23,7 @@ use crate::input_filter::{Filter, ModifierStateListener}; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; +use input::ModifierState; use std::collections::HashSet; // Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h @@ -40,20 +41,6 @@ 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, @@ -61,11 +48,11 @@ pub struct StickyKeysFilter { contributing_devices: HashSet<i32>, /// State describing the current enabled modifiers. This contain both locked and non-locked /// modifier state bits. - modifier_state: u32, + modifier_state: ModifierState, /// 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, + locked_modifier_state: ModifierState, } impl StickyKeysFilter { @@ -78,8 +65,8 @@ impl StickyKeysFilter { next, listener, contributing_devices: HashSet::new(), - modifier_state: 0, - locked_modifier_state: 0, + modifier_state: ModifierState::None, + locked_modifier_state: ModifierState::None, } } } @@ -93,12 +80,12 @@ impl Filter for StickyKeysFilter { // 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 old_modifier_state = ModifierState::from_bits(event.metaState as u32).unwrap(); 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; + (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state).bits() as i32; self.next.notify_key(&new_event); if up && !is_modifier_key(event.keyCode) { modifier_state = @@ -110,10 +97,10 @@ impl Filter for StickyKeysFilter { // 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 { + if locked_modifier_state & modifier_key_mask != ModifierState::None { locked_modifier_state &= !symmetrical_modifier_key_mask; modifier_state &= !symmetrical_modifier_key_mask; - } else if modifier_key_mask & modifier_state != 0 { + } else if modifier_key_mask & modifier_state != ModifierState::None { locked_modifier_state |= modifier_key_mask; modifier_state = (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask; @@ -134,11 +121,12 @@ impl Filter for StickyKeysFilter { // 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 != ModifierState::None + || self.locked_modifier_state != ModifierState::None) { - self.modifier_state = 0; - self.locked_modifier_state = 0; - self.listener.modifier_state_changed(0, 0); + self.modifier_state = ModifierState::None; + self.locked_modifier_state = ModifierState::None; + self.listener.modifier_state_changed(ModifierState::None, ModifierState::None); } self.next.notify_devices_changed(device_infos); } @@ -181,51 +169,53 @@ fn is_ephemeral_modifier_key(keycode: i32) -> bool { ) } -fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 { +fn get_ephemeral_modifier_key_mask(keycode: i32) -> ModifierState { 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, + KEYCODE_ALT_LEFT => ModifierState::AltLeftOn | ModifierState::AltOn, + KEYCODE_ALT_RIGHT => ModifierState::AltRightOn | ModifierState::AltOn, + KEYCODE_SHIFT_LEFT => ModifierState::ShiftLeftOn | ModifierState::ShiftOn, + KEYCODE_SHIFT_RIGHT => ModifierState::ShiftRightOn | ModifierState::ShiftOn, + KEYCODE_CTRL_LEFT => ModifierState::CtrlLeftOn | ModifierState::CtrlOn, + KEYCODE_CTRL_RIGHT => ModifierState::CtrlRightOn | ModifierState::CtrlOn, + KEYCODE_META_LEFT => ModifierState::MetaLeftOn | ModifierState::MetaOn, + KEYCODE_META_RIGHT => ModifierState::MetaRightOn | ModifierState::MetaOn, + _ => ModifierState::None, } } /// Modifier mask including both left and right versions of a modifier key. -fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 { +fn get_symmetrical_modifier_key_mask(keycode: i32) -> ModifierState { match keycode { - KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => { + ModifierState::AltLeftOn | ModifierState::AltRightOn | ModifierState::AltOn + } KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => { - META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON + ModifierState::ShiftLeftOn | ModifierState::ShiftRightOn | ModifierState::ShiftOn } KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => { - META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON + ModifierState::CtrlLeftOn | ModifierState::CtrlRightOn | ModifierState::CtrlOn } KEYCODE_META_LEFT | KEYCODE_META_RIGHT => { - META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON + ModifierState::MetaLeftOn | ModifierState::MetaRightOn | ModifierState::MetaOn } - _ => 0, + _ => ModifierState::None, } } -fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 { +fn clear_ephemeral_modifier_state(modifier_state: ModifierState) -> ModifierState { 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) + & !(ModifierState::AltLeftOn + | ModifierState::AltRightOn + | ModifierState::AltOn + | ModifierState::ShiftLeftOn + | ModifierState::ShiftRightOn + | ModifierState::ShiftOn + | ModifierState::CtrlLeftOn + | ModifierState::CtrlRightOn + | ModifierState::CtrlOn + | ModifierState::MetaLeftOn + | ModifierState::MetaRightOn + | ModifierState::MetaOn) } #[cfg(test)] @@ -237,9 +227,7 @@ mod tests { 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, + KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, }; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use binder::Strong; @@ -247,6 +235,7 @@ mod tests { DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::ModifierState; use std::sync::{Arc, RwLock}; static DEVICE_ID: i32 = 1; @@ -347,30 +336,30 @@ mod tests { 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), + (KEYCODE_ALT_LEFT, ModifierState::AltOn | ModifierState::AltLeftOn), + (KEYCODE_ALT_RIGHT, ModifierState::AltOn | ModifierState::AltRightOn), + (KEYCODE_CTRL_LEFT, ModifierState::CtrlOn | ModifierState::CtrlLeftOn), + (KEYCODE_CTRL_RIGHT, ModifierState::CtrlOn | ModifierState::CtrlRightOn), + (KEYCODE_SHIFT_LEFT, ModifierState::ShiftOn | ModifierState::ShiftLeftOn), + (KEYCODE_SHIFT_RIGHT, ModifierState::ShiftOn | ModifierState::ShiftRightOn), + (KEYCODE_META_LEFT, ModifierState::MetaOn | ModifierState::MetaLeftOn), + (KEYCODE_META_RIGHT, ModifierState::MetaOn | ModifierState::MetaRightOn), ]; 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); + assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); 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); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); // 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); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); @@ -382,8 +371,8 @@ mod tests { 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); + assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); } } @@ -398,14 +387,17 @@ mod tests { 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); + assert_eq!( + test_callbacks.get_last_modifier_state(), + ModifierState::CtrlLeftOn | ModifierState::CtrlOn + ); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); 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); + assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); } #[test] @@ -427,20 +419,26 @@ mod tests { assert_eq!( test_callbacks.get_last_modifier_state(), - META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON + ModifierState::ShiftLeftOn + | ModifierState::ShiftOn + | ModifierState::CtrlLeftOn + | ModifierState::CtrlOn ); assert_eq!( test_callbacks.get_last_locked_modifier_state(), - META_CTRL_LEFT_ON | META_CTRL_ON + ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); 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_modifier_state(), + ModifierState::CtrlLeftOn | ModifierState::CtrlOn + ); assert_eq!( test_callbacks.get_last_locked_modifier_state(), - META_CTRL_LEFT_ON | META_CTRL_ON + ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); } @@ -458,13 +456,13 @@ mod tests { 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 + (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits() ); 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 + (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits() ); } @@ -499,15 +497,18 @@ mod tests { }); 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_modifier_state(), + ModifierState::CtrlLeftOn | ModifierState::CtrlOn + ); assert_eq!( test_callbacks.get_last_locked_modifier_state(), - META_CTRL_LEFT_ON | META_CTRL_ON + ModifierState::CtrlLeftOn | ModifierState::CtrlOn ); 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); + assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None); } fn setup_filter( diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index 530416c7aa..e17ee3a5d9 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -215,6 +215,10 @@ void FakeInputDispatcherPolicy::setStaleEventTimeout(std::chrono::nanoseconds ti mStaleEventTimeout = timeout; } +void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) { + mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; +} + void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -401,6 +405,9 @@ void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplay nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) { + if (mConsumeKeyBeforeDispatching) { + return -1; + } nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); // Clear intercept state so we could dispatch the event in next wake. mInterceptKeyTimeout = 0ms; diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index 2c86146ba3..62ff10f8c8 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -115,6 +115,7 @@ public: void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler); void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); + void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching); private: std::mutex mLock; @@ -144,6 +145,8 @@ private: std::chrono::nanoseconds mStaleEventTimeout = 1000ms; + bool mConsumeKeyBeforeDispatching = false; + BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions; std::condition_variable mNotifyUnhandledKey; diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index 456013e347..d0998ba851 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -77,10 +77,12 @@ void FakePointerController::setCustomPointerIcon(const SpriteIcon& icon) { } void FakePointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) { + mDisplaysToSkipScreenshotFlagChanged = true; mDisplaysToSkipScreenshot.insert(displayId); } void FakePointerController::clearSkipScreenshotFlags() { + mDisplaysToSkipScreenshotFlagChanged = true; mDisplaysToSkipScreenshot.clear(); } @@ -125,13 +127,21 @@ void FakePointerController::assertCustomPointerIconNotSet() { ASSERT_EQ(std::nullopt, mCustomIconStyle); } -void FakePointerController::assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId, - bool isHidden) { - if (isHidden) { - ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end()); - } else { - ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end()); - } +void FakePointerController::assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId) { + ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end()); +} + +void FakePointerController::assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId) { + ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end()); +} + +void FakePointerController::assertSkipScreenshotFlagChanged() { + ASSERT_TRUE(mDisplaysToSkipScreenshotFlagChanged); + mDisplaysToSkipScreenshotFlagChanged = false; +} + +void FakePointerController::assertSkipScreenshotFlagNotChanged() { + ASSERT_FALSE(mDisplaysToSkipScreenshotFlagChanged); } bool FakePointerController::isPointerShown() { diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 8d95f65896..2c76c6214c 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -57,7 +57,10 @@ public: void assertPointerIconNotSet(); void assertCustomPointerIconSet(PointerIconStyle iconId); void assertCustomPointerIconNotSet(); - void assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId, bool isHidden); + void assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId); + void assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId); + void assertSkipScreenshotFlagChanged(); + void assertSkipScreenshotFlagNotChanged(); bool isPointerShown(); private: @@ -81,6 +84,7 @@ private: std::map<ui::LogicalDisplayId, std::vector<int32_t>> mSpotsByDisplay; std::unordered_set<ui::LogicalDisplayId> mDisplaysToSkipScreenshot; + bool mDisplaysToSkipScreenshotFlagChanged{false}; }; } // namespace android diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp index ff9bd9e75b..34c81fcf02 100644 --- a/services/inputflinger/tests/HardwareStateConverter_test.cpp +++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp @@ -81,7 +81,7 @@ protected: event.type = type; event.code = code; event.value = value; - std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(&event); + std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(event); EXPECT_FALSE(schs.has_value()); } @@ -93,7 +93,7 @@ protected: event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; - return mConverter->processRawEvent(&event); + return mConverter->processRawEvent(event); } std::shared_ptr<FakeEventHub> mFakeEventHub; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 8de28c680f..56a05a3a3c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -610,28 +610,6 @@ static NotifyKeyArgs generateKeyArgs( return args; } -static NotifyKeyArgs generateSystemShortcutArgs( - int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) { - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - // Define a valid key event. - 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; -} - -static NotifyKeyArgs generateAssistantKeyArgs( - int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) { - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - // Define a valid key event. - 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; -} - [[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, ui::LogicalDisplayId displayId, const std::vector<PointF>& points) { @@ -6628,17 +6606,18 @@ TEST_F(InputDispatcherTest, FocusedWindow_DisableUserActivity) { window->consumeFocusEvent(true); - mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); // Window should receive key down event. window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT); - // Should have poked user activity + // Should have not poked user activity mDispatcher->waitForIdle(); mFakePolicy->assertUserActivityNotPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) { +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyConsumedKey) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", @@ -6650,31 +6629,36 @@ TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) { window->consumeFocusEvent(true); + mFakePolicy->setConsumeKeyBeforeDispatching(true); + mDispatcher->notifyKey( - generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); mDispatcher->waitForIdle(); - // System key is not passed down + // Key is not passed down window->assertNoEvents(); // Should have poked user activity mFakePolicy->assertUserActivityPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) { +TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", ui::LogicalDisplayId::DEFAULT); + window->setDisableUserActivity(true); window->setFocusable(true); mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); setFocusedWindow(window); window->consumeFocusEvent(true); + mFakePolicy->setConsumeKeyBeforeDispatching(true); + mDispatcher->notifyKey( - generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); mDispatcher->waitForIdle(); // System key is not passed down @@ -6684,30 +6668,39 @@ TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) { mFakePolicy->assertUserActivityPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) { +class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> {}; + +TEST_P(DisableUserActivityInputDispatcherTest, NotPassedToUserUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", ui::LogicalDisplayId::DEFAULT); - window->setDisableUserActivity(true); + window->setDisableUserActivity(GetParam()); + window->setFocusable(true); mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); setFocusedWindow(window); window->consumeFocusEvent(true); - mDispatcher->notifyKey( - generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_A) + .policyFlags(0) + .build()); mDispatcher->waitForIdle(); - // System key is not passed down + // Key is not passed down window->assertNoEvents(); - // Should have poked user activity - mFakePolicy->assertUserActivityPoked(); + // Should not have poked user activity + mFakePolicy->assertUserActivityNotPoked(); } +INSTANTIATE_TEST_CASE_P(DisableUserActivity, DisableUserActivityInputDispatcherTest, + ::testing::Bool()); + TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -11054,6 +11047,37 @@ TEST_F(InputDispatcherPointerCaptureTests, MouseHoverAndPointerCapture) { mWindow->assertNoEvents(); } +TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) { + // The default display is the focused display to begin with. + requestAndVerifyPointerCapture(mWindow, true); + + // Move the second window to a second display, make it the focused window on that display. + mSecondWindow->editInfo()->displayId = SECOND_DISPLAY_ID; + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mSecondWindow); + mSecondWindow->consumeFocusEvent(true); + + mWindow->assertNoEvents(); + + // The second window cannot gain capture because it is not on the focused display. + mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true); + mFakePolicy->assertSetPointerCaptureNotCalled(); + mSecondWindow->assertNoEvents(); + + // Make the second display the focused display. + mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + + // This causes the first window to lose pointer capture, and it's unable to request capture. + mWindow->consumeCaptureEvent(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); + + mDispatcher->requestPointerCapture(mWindow->getToken(), true); + mFakePolicy->assertSetPointerCaptureNotCalled(); + + // The second window is now able to gain pointer capture successfully. + requestAndVerifyPointerCapture(mSecondWindow, true); +} + using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests; TEST_F(InputDispatcherPointerCaptureDeathTest, diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index fea1349b88..b5c9232a78 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -104,7 +104,7 @@ std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, i event.type = type; event.code = code; event.value = value; - return mMapper->process(&event); + return mMapper->process(event); } const char* InputMapperTest::DEVICE_NAME = "device"; @@ -195,7 +195,7 @@ std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when event.type = type; event.code = code; event.value = value; - std::list<NotifyArgs> processArgList = mapper.process(&event); + std::list<NotifyArgs> processArgList = mapper.process(event); for (const NotifyArgs& args : processArgList) { mFakeListener->notify(args); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8536ff0676..fe238f3087 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -309,9 +309,9 @@ private: return {}; } - std::list<NotifyArgs> process(const RawEvent* rawEvent) override { + std::list<NotifyArgs> process(const RawEvent& rawEvent) override { std::scoped_lock<std::mutex> lock(mLock); - mLastEvent = *rawEvent; + mLastEvent = rawEvent; mProcessWasCalled = true; mStateChangedCondition.notify_all(); return mProcessResult; @@ -3314,6 +3314,10 @@ TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { class KeyboardInputMapperTest : public InputMapperTest { protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | + InputDeviceClass::ALPHAKEY); + } const std::string UNIQUE_ID = "local:0"; const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty"); void prepareDisplay(ui::Rotation orientation); @@ -3354,8 +3358,7 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, TEST_F(KeyboardInputMapperTest, GetSources) { KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources()); } @@ -3370,8 +3373,7 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); @@ -3471,8 +3473,7 @@ TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Key down by scan code. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); @@ -3493,8 +3494,7 @@ TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); NotifyKeyArgs args; // Key down @@ -3516,8 +3516,7 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); @@ -3557,8 +3556,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); prepareDisplay(ui::ROTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -3579,8 +3577,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); prepareDisplay(ui::ROTATION_0); ASSERT_NO_FATAL_FAILURE( @@ -3651,8 +3648,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); NotifyKeyArgs args; // Display id should be LogicalDisplayId::INVALID without any display configuration. @@ -3677,8 +3673,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); NotifyKeyArgs args; // Display id should be LogicalDisplayId::INVALID without any display configuration. @@ -3705,8 +3700,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1); ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); @@ -3717,8 +3711,7 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) { KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z); ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y)) @@ -3730,8 +3723,7 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) { TEST_F(KeyboardInputMapperTest, GetScanCodeState) { KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1); ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); @@ -3742,8 +3734,7 @@ TEST_F(KeyboardInputMapperTest, GetScanCodeState) { TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); @@ -3762,8 +3753,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); @@ -3828,8 +3818,7 @@ TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) { mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Meta state should be AMETA_NONE after reset std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME); @@ -3878,16 +3867,14 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); KeyboardInputMapper& mapper2 = device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, mFakePolicy ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + AINPUT_SOURCE_KEYBOARD); std::list<NotifyArgs> unused = device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), /*changes=*/{}); @@ -3949,8 +3936,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); @@ -4000,8 +3986,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, mFakePolicy ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + AINPUT_SOURCE_KEYBOARD); std::list<NotifyArgs> unused = device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), /*changes=*/{}); @@ -4020,11 +4005,9 @@ TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); // Suppose we have two mappers. (DPAD + KEYBOARD) - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD, - AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Initial metastate is AMETA_NONE. ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); @@ -4042,8 +4025,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper1 = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // keyboard 2. const std::string USB2 = "USB2"; @@ -4065,8 +4047,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, mFakePolicy ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + AINPUT_SOURCE_KEYBOARD); std::list<NotifyArgs> unused = device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), /*changes=*/{}); @@ -4122,8 +4103,7 @@ TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) { mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); // Key down by scan code. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); NotifyKeyArgs args; @@ -4148,8 +4128,7 @@ TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) { } TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) { - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), /*changes=*/{}); @@ -4180,8 +4159,7 @@ TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) { RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}); // Configuration - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); InputReaderConfiguration config; std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{}); @@ -4192,8 +4170,7 @@ TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) { TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); NotifyKeyArgs args; // Key down @@ -4202,14 +4179,27 @@ TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) { ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags); } -// --- KeyboardInputMapperTest_ExternalDevice --- +// --- KeyboardInputMapperTest_ExternalAlphabeticDevice --- -class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { +class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest { protected: - void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); } + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | + InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL); + } +}; + +// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice --- + +class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | + InputDeviceClass::EXTERNAL); + } }; -TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) { +TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) { // For external devices, keys will trigger wake on key down. Media keys should also trigger // wake if triggered from external devices. @@ -4219,8 +4209,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); NotifyKeyArgs args; @@ -4248,7 +4237,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) { +TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) { // For external devices, keys will trigger wake on key down. Media keys should not trigger // wake if triggered from external non-alphaebtic keyboard (e.g. headsets). @@ -4257,8 +4246,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboa POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); NotifyKeyArgs args; @@ -4278,7 +4266,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboa ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { +TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) { // Tv Remote key's wake behavior is prescribed by the keylayout file. mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); @@ -4287,8 +4275,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); NotifyKeyArgs args; @@ -5485,6 +5472,9 @@ TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, pressure, size, tool, tool, tool, tool, orientation, distance)); ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT)); + ASSERT_EQ(args.flags, + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION); } TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { @@ -7927,6 +7917,7 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation, distance)); + ASSERT_EQ(args.flags, AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { @@ -10595,24 +10586,66 @@ TEST_F(LightControllerTest, MultiColorRGBKeyboardBacklight) { ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR); } +TEST_F(LightControllerTest, SonyPlayerIdLight) { + RawLightInfo info1 = {.id = 1, + .name = "sony1", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info2 = {.id = 2, + .name = "sony2", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info3 = {.id = 3, + .name = "sony3", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info4 = {.id = 4, + .name = "sony4", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(info1.id, std::move(info1)); + mFakeEventHub->addRawLightInfo(info2.id, std::move(info2)); + mFakeEventHub->addRawLightInfo(info3.id, std::move(info3)); + mFakeEventHub->addRawLightInfo(info4.id, std::move(info4)); + + PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); + InputDeviceInfo info; + controller.populateDeviceInfo(&info); + std::vector<InputDeviceLightInfo> lights = info.getLights(); + ASSERT_EQ(1U, lights.size()); + ASSERT_STREQ("sony", lights[0].name.c_str()); + ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type); + ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); + ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); + + ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR)); + ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID)); + ASSERT_EQ(controller.getLightPlayerId(lights[0].id).value_or(-1), LIGHT_PLAYER_ID); + ASSERT_STREQ("sony", lights[0].name.c_str()); +} + TEST_F(LightControllerTest, PlayerIdLight) { RawLightInfo info1 = {.id = 1, - .name = "player1", + .name = "player-1", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS, .path = ""}; RawLightInfo info2 = {.id = 2, - .name = "player2", + .name = "player-2", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS, .path = ""}; RawLightInfo info3 = {.id = 3, - .name = "player3", + .name = "player-3", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS, .path = ""}; RawLightInfo info4 = {.id = 4, - .name = "player4", + .name = "player-4", .maxBrightness = 255, .flags = InputLightClass::BRIGHTNESS, .path = ""}; @@ -10626,6 +10659,7 @@ TEST_F(LightControllerTest, PlayerIdLight) { controller.populateDeviceInfo(&info); std::vector<InputDeviceLightInfo> lights = info.getLights(); ASSERT_EQ(1U, lights.size()); + ASSERT_STREQ("player", lights[0].name.c_str()); ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type); ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)); ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB)); diff --git a/services/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp index 32acb5f288..a9d370aedd 100644 --- a/services/inputflinger/tests/InputTraceSession.cpp +++ b/services/inputflinger/tests/InputTraceSession.cpp @@ -20,6 +20,9 @@ #include <android-base/logging.h> #include <gtest/gtest.h> #include <input/PrintTools.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <utility> @@ -30,6 +33,8 @@ using perfetto::protos::pbzero::AndroidInputEventConfig; using perfetto::protos::pbzero::AndroidKeyEvent; using perfetto::protos::pbzero::AndroidMotionEvent; using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent; +using perfetto::protos::pbzero::WinscopeExtensions; +using perfetto::protos::pbzero::WinscopeExtensionsImpl; // These operator<< definitions must be in the global namespace for them to be accessible to the // GTEST library. They cannot be in the anonymous namespace. @@ -85,38 +90,45 @@ auto decodeTrace(const std::string& rawTrace) { Trace::Decoder trace{rawTrace}; if (trace.has_packet()) { - auto it = trace.packet(); - while (it) { + for (auto it = trace.packet(); it; it++) { TracePacket::Decoder packet{it->as_bytes()}; - if (packet.has_android_input_event()) { - AndroidInputEvent::Decoder event{packet.android_input_event()}; - if (event.has_dispatcher_motion_event()) { - tracedMotions.emplace_back(event.dispatcher_motion_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_motion_event_redacted()) { - tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(), - /*redacted=*/true); - } - if (event.has_dispatcher_key_event()) { - tracedKeys.emplace_back(event.dispatcher_key_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_key_event_redacted()) { - tracedKeys.emplace_back(event.dispatcher_key_event_redacted(), - /*redacted=*/true); - } - if (event.has_dispatcher_window_dispatch_event()) { - tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_window_dispatch_event_redacted()) { - tracedWindowDispatches - .emplace_back(event.dispatcher_window_dispatch_event_redacted(), - /*redacted=*/true); - } + if (!packet.has_winscope_extensions()) { + continue; + } + + WinscopeExtensions::Decoder extensions{packet.winscope_extensions()}; + const auto& field = + extensions.Get(WinscopeExtensionsImpl::kAndroidInputEventFieldNumber); + if (!field.valid()) { + continue; + } + + AndroidInputEvent::Decoder event{field.as_bytes()}; + if (event.has_dispatcher_motion_event()) { + tracedMotions.emplace_back(event.dispatcher_motion_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_motion_event_redacted()) { + tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_key_event()) { + tracedKeys.emplace_back(event.dispatcher_key_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_key_event_redacted()) { + tracedKeys.emplace_back(event.dispatcher_key_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_window_dispatch_event()) { + tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_window_dispatch_event_redacted()) { + tracedWindowDispatches + .emplace_back(event.dispatcher_window_dispatch_event_redacted(), + /*redacted=*/true); } - it++; } } return std::tuple{std::move(tracedMotions), std::move(tracedKeys), diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h index ed20bc8343..bda552165e 100644 --- a/services/inputflinger/tests/InputTraceSession.h +++ b/services/inputflinger/tests/InputTraceSession.h @@ -22,7 +22,6 @@ #include <gtest/gtest.h> #include <input/Input.h> #include <perfetto/config/android/android_input_event_config.pbzero.h> -#include <perfetto/trace/android/android_input_event.pbzero.h> #include <perfetto/trace/trace.pbzero.h> #include <perfetto/tracing.h> #include <variant> diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp index 617d67f34d..2ccd93ec4c 100644 --- a/services/inputflinger/tests/InputTracingTest.cpp +++ b/services/inputflinger/tests/InputTracingTest.cpp @@ -30,6 +30,8 @@ #include <gtest/gtest.h> #include <input/Input.h> #include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <perfetto/trace/trace.pbzero.h> #include <private/android_filesystem_config.h> #include <map> diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 6389cdc5fb..44417246d2 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -27,7 +27,9 @@ #include <EventHub.h> #include <InputReaderBase.h> +#include <InputReaderContext.h> #include <NotifyArgs.h> +#include <PointerChoreographerPolicyInterface.h> #include <StylusState.h> #include <VibrationElement.h> #include <android-base/logging.h> @@ -36,6 +38,7 @@ #include <input/InputDevice.h> #include <input/KeyCharacterMap.h> #include <input/KeyLayoutMap.h> +#include <input/KeyboardClassifier.h> #include <input/PropertyMap.h> #include <input/TouchVideoFrame.h> #include <input/VirtualKeyMap.h> @@ -75,8 +78,11 @@ public: MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when)); MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ()); + KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; }; + private: int32_t mGeneration = 0; + std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>(); }; class MockEventHubInterface : public EventHubInterface { @@ -174,4 +180,12 @@ public: MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override)); }; +class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolicyInterface { +public: + MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, createPointerController, + (PointerControllerInterface::ControllerType), (override)); + MOCK_METHOD(void, notifyPointerDisplayIdChanged, + (ui::LogicalDisplayId displayId, const FloatPoint& position), (override)); +}; + } // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 031b77d67a..ada841d28d 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -67,8 +67,7 @@ protected: EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get())); mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, - AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); + AINPUT_SOURCE_KEYBOARD); } void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) { diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp index 5e67506b48..b441a23803 100644 --- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp +++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp @@ -38,7 +38,7 @@ protected: event.type = type; event.code = code; event.value = value; - mMotionAccumulator.process(&event); + mMotionAccumulator.process(event); } }; diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 69a7382cd4..3f2d6ec45c 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -22,6 +22,7 @@ #include <vector> #include "FakePointerController.h" +#include "InterfaceMocks.h" #include "NotifyArgsBuilders.h" #include "TestEventMatchers.h" #include "TestInputListener.h" @@ -89,10 +90,55 @@ static std::vector<DisplayViewport> createViewports(std::vector<ui::LogicalDispl // --- PointerChoreographerTest --- -class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface { +class TestPointerChoreographer : public PointerChoreographer { +public: + TestPointerChoreographer(InputListenerInterface& inputListener, + PointerChoreographerPolicyInterface& policy, + sp<gui::WindowInfosListener>& windowInfoListener, + const std::vector<gui::WindowInfo>& mInitialWindowInfos); +}; + +TestPointerChoreographer::TestPointerChoreographer( + InputListenerInterface& inputListener, PointerChoreographerPolicyInterface& policy, + sp<gui::WindowInfosListener>& windowInfoListener, + const std::vector<gui::WindowInfo>& mInitialWindowInfos) + : PointerChoreographer( + inputListener, policy, + [&windowInfoListener, + &mInitialWindowInfos](const sp<android::gui::WindowInfosListener>& listener) { + windowInfoListener = listener; + return mInitialWindowInfos; + }, + [&windowInfoListener](const sp<android::gui::WindowInfosListener>& listener) { + windowInfoListener = nullptr; + }) {} + +class PointerChoreographerTest : public testing::Test { protected: TestInputListener mTestListener; - PointerChoreographer mChoreographer{mTestListener, *this}; + sp<gui::WindowInfosListener> mRegisteredWindowInfoListener; + std::vector<gui::WindowInfo> mInjectedInitialWindowInfos; + testing::NiceMock<MockPointerChoreographerPolicyInterface> mMockPolicy; + TestPointerChoreographer mChoreographer{mTestListener, mMockPolicy, + mRegisteredWindowInfoListener, + mInjectedInitialWindowInfos}; + + void SetUp() override { + // flag overrides + input_flags::hide_pointer_indicators_for_secure_windows(true); + + ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) { + std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>(); + EXPECT_FALSE(pc->isPointerShown()); + mCreatedControllers.emplace_back(type, pc); + return pc; + }); + + ON_CALL(mMockPolicy, notifyPointerDisplayIdChanged) + .WillByDefault([this](ui::LogicalDisplayId displayId, const FloatPoint& position) { + mPointerDisplayIdNotified = displayId; + }); + } std::shared_ptr<FakePointerController> assertPointerControllerCreated( ControllerType expectedType) { @@ -131,23 +177,20 @@ protected: void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); } + void assertWindowInfosListenerRegistered() { + ASSERT_NE(nullptr, mRegisteredWindowInfoListener) + << "WindowInfosListener was not registered"; + } + + void assertWindowInfosListenerNotRegistered() { + ASSERT_EQ(nullptr, mRegisteredWindowInfoListener) + << "WindowInfosListener was not unregistered"; + } + private: std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>> mCreatedControllers; std::optional<ui::LogicalDisplayId> mPointerDisplayIdNotified; - - std::shared_ptr<PointerControllerInterface> createPointerController( - ControllerType type) override { - std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>(); - EXPECT_FALSE(pc->isPointerShown()); - mCreatedControllers.emplace_back(type, pc); - return pc; - } - - void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId, - const FloatPoint& position) override { - mPointerDisplayIdNotified = displayId; - } }; TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { @@ -1636,16 +1679,36 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) { firstMousePc->assertPointerIconNotSet(); } -using HidePointerForPrivacySensitiveDisplaysFixtureParam = +using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam = std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder, std::function<void(PointerChoreographer&)>, int32_t /*action*/>; -class HidePointerForPrivacySensitiveDisplaysTestFixture +class SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture : public PointerChoreographerTest, - public ::testing::WithParamInterface<HidePointerForPrivacySensitiveDisplaysFixtureParam> {}; + public ::testing::WithParamInterface< + SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam> { +protected: + void initializePointerDevice(const PointerBuilder& pointerBuilder, const uint32_t source, + const std::function<void(PointerChoreographer&)> onControllerInit, + const int32_t action) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Add appropriate pointer device + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); + onControllerInit(mChoreographer); + + // Emit input events to create PointerController + mChoreographer.notifyMotion(MotionArgsBuilder(action, source) + .pointer(pointerBuilder) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + } +}; INSTANTIATE_TEST_SUITE_P( - PointerChoreographerTest, HidePointerForPrivacySensitiveDisplaysTestFixture, + PointerChoreographerTest, SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, ::testing::Values( std::make_tuple( "TouchSpots", AINPUT_SOURCE_TOUCHSCREEN, ControllerType::TOUCH, @@ -1663,44 +1726,205 @@ INSTANTIATE_TEST_SUITE_P( "DrawingTablet", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS, ControllerType::MOUSE, STYLUS_POINTER, [](PointerChoreographer& pc) {}, AMOTION_EVENT_ACTION_HOVER_ENTER)), - [](const testing::TestParamInfo<HidePointerForPrivacySensitiveDisplaysFixtureParam>& p) { + [](const testing::TestParamInfo< + SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam>& p) { return std::string{std::get<0>(p.param)}; }); -TEST_P(HidePointerForPrivacySensitiveDisplaysTestFixture, - HidesPointerOnMirroredDisplaysForPrivacySensitiveDisplay) { +TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, + WindowInfosListenerIsOnlyRegisteredWhenRequired) { const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] = GetParam(); - input_flags::hide_pointer_indicators_for_secure_windows(true); + assertWindowInfosListenerNotRegistered(); + + // Listener should registered when a pointer device is added + initializePointerDevice(pointerBuilder, source, onControllerInit, action); + assertWindowInfosListenerRegistered(); + + mChoreographer.notifyInputDevicesChanged({}); + assertWindowInfosListenerNotRegistered(); +} + +TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, + InitialDisplayInfoIsPopulatedForListener) { + const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] = + GetParam(); + // listener should not be registered if there is no pointer device + assertWindowInfosListenerNotRegistered(); + + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + mInjectedInitialWindowInfos = {windowInfo}; + + initializePointerDevice(pointerBuilder, source, onControllerInit, action); + assertWindowInfosListenerRegistered(); + + // Pointer indicators should be hidden based on the initial display info + auto pc = assertPointerControllerCreated(controllerType); + pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + + // un-marking the privacy sensitive display should reset the state + windowInfo.inputConfig.clear(); + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); +} + +TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, + SkipsPointerScreenshotForPrivacySensitiveWindows) { + const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] = + GetParam(); + initializePointerDevice(pointerBuilder, source, onControllerInit, action); + + // By default pointer indicators should not be hidden + auto pc = assertPointerControllerCreated(controllerType); + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + + // marking a display privacy sensitive should set flag to hide pointer indicators on the + // display screenshot + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + assertWindowInfosListenerRegistered(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + + // un-marking the privacy sensitive display should reset the state + windowInfo.inputConfig.clear(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); +} + +TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, + DoesNotSkipPointerScreenshotForHiddenPrivacySensitiveWindows) { + const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] = + GetParam(); + initializePointerDevice(pointerBuilder, source, onControllerInit, action); + + // By default pointer indicators should not be hidden + auto pc = assertPointerControllerCreated(controllerType); + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_VISIBLE; + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + assertWindowInfosListenerRegistered(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); +} + +TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, + DoesNotUpdateControllerForUnchangedPrivacySensitiveWindows) { + const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] = + GetParam(); + initializePointerDevice(pointerBuilder, source, onControllerInit, action); + + auto pc = assertPointerControllerCreated(controllerType); + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + assertWindowInfosListenerRegistered(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + gui::WindowInfo windowInfo2 = windowInfo; + windowInfo2.inputConfig.clear(); + pc->assertSkipScreenshotFlagChanged(); + + // controller should not be updated if there are no changes in privacy sensitive windows + mRegisteredWindowInfoListener->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo, windowInfo2}, + {displayInfo}, + /*vsyncId=*/0, + /*timestamp=*/0}); + pc->assertSkipScreenshotFlagNotChanged(); +} + +TEST_F_WITH_FLAGS( + PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + hide_pointer_indicators_for_secure_windows))) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - // Add appropriate pointer device + // Add a first mouse device mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); - onControllerInit(mChoreographer); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); - // Emit input events to create PointerController - mChoreographer.notifyMotion(MotionArgsBuilder(action, source) - .pointer(pointerBuilder) + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) .build()); - // By default pointer indicators should not be hidden - auto pc = assertPointerControllerCreated(controllerType); - pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false); - pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false); + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + assertWindowInfosListenerRegistered(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); - // marking a display privacy sensitive should set flag to hide pointer indicators on the - // corresponding mirrored display - mChoreographer.onPrivacySensitiveDisplaysChanged(/*privacySensitiveDisplays=*/{DISPLAY_ID}); - pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/true); - pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + + // Add a second touch device and controller + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}}); + mChoreographer.setShowTouchesEnabled(true); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(FIRST_TOUCH_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + + // Pointer indicators should be hidden for this controller by default + auto pc2 = assertPointerControllerCreated(ControllerType::TOUCH); + pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); // un-marking the privacy sensitive display should reset the state - mChoreographer.onPrivacySensitiveDisplaysChanged(/*privacySensitiveDisplays=*/{}); - pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false); - pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false); + windowInfo.inputConfig.clear(); + mRegisteredWindowInfoListener + ->onWindowInfosChanged(/*windowInfosUpdate=*/ + {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); + + pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); + pc2->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID); + pc2->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID); } TEST_P(StylusTestFixture, SetsPointerIconForStylus) { @@ -2070,4 +2294,41 @@ TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) { assertPointerControllerRemoved(pc); } +class PointerChoreographerWindowInfoListenerTest : public testing::Test {}; + +TEST_F_WITH_FLAGS( + PointerChoreographerWindowInfoListenerTest, + doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + hide_pointer_indicators_for_secure_windows))) { + sp<android::gui::WindowInfosListener> registeredListener; + sp<android::gui::WindowInfosListener> localListenerCopy; + { + testing::NiceMock<MockPointerChoreographerPolicyInterface> mockPolicy; + EXPECT_CALL(mockPolicy, createPointerController(ControllerType::MOUSE)) + .WillOnce(testing::Return(std::make_shared<FakePointerController>())); + TestInputListener testListener; + std::vector<gui::WindowInfo> injectedInitialWindowInfos; + TestPointerChoreographer testChoreographer{testListener, mockPolicy, registeredListener, + injectedInitialWindowInfos}; + testChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Add mouse to create controller and listener + testChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + + ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered"; + localListenerCopy = registeredListener; + } + ASSERT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered"; + + gui::WindowInfo windowInfo; + windowInfo.displayId = DISPLAY_ID; + windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; + gui::DisplayInfo displayInfo; + displayInfo.displayId = DISPLAY_ID; + localListenerCopy->onWindowInfosChanged( + /*windowInfosUpdate=*/{{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0}); +} + } // namespace android diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp index af20a271b8..836151787c 100644 --- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp @@ -81,7 +81,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, InputReaderConfiguration::Change(0)); RawEvent rawEvent = getFuzzedRawEvent(*fdp); - unused += mapper.process(&rawEvent); + unused += mapper.process(rawEvent); }, [&]() -> void { std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp index 922cbdfb87..9e02502b6b 100644 --- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp @@ -50,11 +50,10 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzInputReaderContext context(eventHub, fdp); InputDevice device = getFuzzedInputDevice(*fdp, &context); - KeyboardInputMapper& mapper = getMapperForDevice< - ThreadSafeFuzzedDataProvider, - KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{}, - /*source=*/fdp->ConsumeIntegral<uint32_t>(), - /*keyboardType=*/fdp->ConsumeIntegral<int32_t>()); + KeyboardInputMapper& mapper = + getMapperForDevice<ThreadSafeFuzzedDataProvider, + KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{}, + /*source=*/fdp->ConsumeIntegral<uint32_t>()); // Loop through mapper operations until randomness is exhausted. while (fdp->remaining_bytes() > 0) { @@ -80,7 +79,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { }, [&]() -> void { RawEvent rawEvent = getFuzzedRawEvent(*fdp); - std::list<NotifyArgs> unused = mapper.process(&rawEvent); + std::list<NotifyArgs> unused = mapper.process(rawEvent); }, [&]() -> void { mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(), diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 25f2f2ec76..ff425ddfb7 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -338,9 +338,11 @@ public: void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; }; nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; }; + KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; } private: nsecs_t mLastKeyDownTimestamp; + std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>(); }; template <class Fdp> diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp index d3f66900da..f29577d7f0 100644 --- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp @@ -100,7 +100,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { }, [&]() -> void { RawEvent rawEvent = getFuzzedRawEvent(*fdp); - std::list<NotifyArgs> unused = mapper.process(&rawEvent); + std::list<NotifyArgs> unused = mapper.process(rawEvent); }, [&]() -> void { mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(), diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp index ac2030afd3..a42d447fe3 100644 --- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp @@ -44,7 +44,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { [&]() -> void { mapper.getSources(); }, [&]() -> void { RawEvent rawEvent = getFuzzedRawEvent(*fdp); - std::list<NotifyArgs> unused = mapper.process(&rawEvent); + std::list<NotifyArgs> unused = mapper.process(rawEvent); }, [&]() -> void { mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(), diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index 643e8b9f97..c620032eef 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -176,7 +176,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { }, [&]() -> void { RawEvent event = getFuzzedRawEvent(*fdp); - std::list<NotifyArgs> unused = mapper.process(&event); + std::list<NotifyArgs> unused = mapper.process(event); }, })(); } diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index afaf0ae84f..f4b0265afb 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -84,6 +84,7 @@ cc_library { "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], static_libs: [ diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS index 90c233030e..7347ac74e6 100644 --- a/services/sensorservice/OWNERS +++ b/services/sensorservice/OWNERS @@ -1,3 +1 @@ -arthuri@google.com bduddie@google.com -stange@google.com diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig index 5b499a8fd8..7abfbaab07 100644 --- a/services/sensorservice/senserservice_flags.aconfig +++ b/services/sensorservice/senserservice_flags.aconfig @@ -27,4 +27,4 @@ flag { namespace: "sensors" description: "When this flag is enabled, sensor service will only erase dynamic sensor data at the end of the threadLoop to prevent race condition." bug: "329020894" -}
\ No newline at end of file +} diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 8ca796e93d..1b6c598372 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -85,6 +85,7 @@ cc_defaults { "libui", "libutils", "libSurfaceFlingerProp", + "libaconfig_storage_read_api_cc" ], static_libs: [ "iinputflinger_aidl_lib_static", @@ -98,6 +99,7 @@ cc_defaults { "libscheduler", "libserviceutils", "libshaders", + "libsurfaceflingerflags", "libtimestats", "libtonemap", ], diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 38cf05327f..a57e626224 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -474,7 +474,6 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool sh features |= RefreshRateOverlay::Features::SetByHwc; } - // TODO(b/296636258) Update to use the render rate range in VRR mode. const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); mRefreshRateOverlay = RefreshRateOverlay::create(fpsRange, features); if (mRefreshRateOverlay) { @@ -489,6 +488,9 @@ void DisplayDevice::updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, ATRACE_CALL(); if (mRefreshRateOverlay) { if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) { + if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) { + refreshRate = renderFps; + } mRefreshRateOverlay->changeRefreshRate(refreshRate, renderFps); } else { mRefreshRateOverlay->changeRenderRate(renderFps); diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index 2b20de38d7..39a6b777bb 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -260,27 +260,36 @@ void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached); } -void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) { - if (root->mLayer) { - attachToRelativeParent(root); - } - for (auto& [child, childVariant] : root->mChildren) { - if (childVariant == LayerHierarchy::Variant::Detached || - childVariant == LayerHierarchy::Variant::Attached) { - attachHierarchyToRelativeParent(child); +std::vector<LayerHierarchy*> LayerHierarchyBuilder::getDescendants(LayerHierarchy* root) { + std::vector<LayerHierarchy*> hierarchies; + hierarchies.push_back(root); + std::vector<LayerHierarchy*> descendants; + for (size_t i = 0; i < hierarchies.size(); i++) { + LayerHierarchy* hierarchy = hierarchies[i]; + if (hierarchy->mLayer) { + descendants.push_back(hierarchy); + } + for (auto& [child, childVariant] : hierarchy->mChildren) { + if (childVariant == LayerHierarchy::Variant::Detached || + childVariant == LayerHierarchy::Variant::Attached) { + hierarchies.push_back(child); + } } } + return descendants; } -void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) { - if (root->mLayer) { - detachFromRelativeParent(root); +void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) { + std::vector<LayerHierarchy*> hierarchiesToAttach = getDescendants(root); + for (LayerHierarchy* hierarchy : hierarchiesToAttach) { + attachToRelativeParent(hierarchy); } - for (auto& [child, childVariant] : root->mChildren) { - if (childVariant == LayerHierarchy::Variant::Detached || - childVariant == LayerHierarchy::Variant::Attached) { - detachHierarchyFromRelativeParent(child); - } +} + +void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) { + std::vector<LayerHierarchy*> hierarchiesToDetach = getDescendants(root); + for (LayerHierarchy* hierarchy : hierarchiesToDetach) { + detachFromRelativeParent(hierarchy); } } diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index 69710be8df..d023f9e9f5 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -218,6 +218,7 @@ private: void detachFromParent(LayerHierarchy*); void attachToRelativeParent(LayerHierarchy*); void detachFromRelativeParent(LayerHierarchy*); + std::vector<LayerHierarchy*> getDescendants(LayerHierarchy*); void attachHierarchyToRelativeParent(LayerHierarchy*); void detachHierarchyFromRelativeParent(LayerHierarchy*); void init(const std::vector<std::unique_ptr<RequestedLayerState>>&); diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp index 4b0618e5aa..dd5e8bd007 100644 --- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -152,6 +152,10 @@ void LayerLifecycleManager::onHandlesDestroyed( if (swapErase(linkedLayer->mirrorIds, layer.id)) { linkedLayer->changes |= RequestedLayerState::Changes::Mirror; } + if (linkedLayer->layerIdToMirror == layer.id) { + linkedLayer->layerIdToMirror = UNASSIGNED_LAYER_ID; + linkedLayer->changes |= RequestedLayerState::Changes::Mirror; + } if (linkedLayer->touchCropId == layer.id) { linkedLayer->touchCropId = UNASSIGNED_LAYER_ID; } diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index b40f3323bc..9527a997df 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -200,19 +200,13 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps) -> c BufferCache::const_iterator it = mBufferCache.find({refreshRate.getIntValue(), renderFps.getIntValue(), transformHint}); if (it == mBufferCache.end()) { - // HWC minFps is not known by the framework in order - // to consider lower rates we set minFps to 0. - const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue(); const int maxFps = mFpsRange.max.getIntValue(); - // Clamp to the range. The current refreshRate may be outside of this range if the display - // has changed its set of supported refresh rates. - const int displayIntFps = std::clamp(refreshRate.getIntValue(), minFps, maxFps); + // Clamp to supported refresh rate range: the current refresh rate may be outside of this + // range if the display has changed its set of supported refresh rates. + const int refreshIntFps = std::clamp(refreshRate.getIntValue(), 0, maxFps); const int renderIntFps = renderFps.getIntValue(); - - // Ensure non-zero range to avoid division by zero. - const float fpsScale = - static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps); + const float fpsScale = static_cast<float>(refreshIntFps) / maxFps; constexpr SkColor kMinFpsColor = SK_ColorRED; constexpr SkColor kMaxFpsColor = SK_ColorGREEN; @@ -228,9 +222,9 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps) -> c const SkColor color = colorBase.toSkColor(); - auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures); + auto buffers = draw(refreshIntFps, renderIntFps, color, transformHint, mFeatures); it = mBufferCache - .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers)) + .try_emplace({refreshIntFps, renderIntFps, transformHint}, std::move(buffers)) .first; } diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index 93ec36e7f8..b2896f07dd 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -65,7 +65,7 @@ private: using Buffers = std::vector<sp<GraphicBuffer>>; - static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags, + static Buffers draw(int refreshRate, int renderFps, SkColor, ui::Transform::RotationFlags, ftl::Flags<Features>); static void drawNumber(int number, int left, SkColor, SkCanvas&); diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 2b4e234604..5add290e96 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -315,39 +315,15 @@ void RegionSamplingThread::captureSample() { return true; }; - std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots; - if (mFlinger.mLayerLifecycleManagerEnabled) { - auto filterFn = [&](const frontend::LayerSnapshot& snapshot, - bool& outStopTraversal) -> bool { - const Rect bounds = - frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds), - snapshot.transparentRegionHint); - const ui::Transform transform = snapshot.geomLayerTransform; - return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform, - outStopTraversal); - }; - getLayerSnapshots = - mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, - filterFn); - } else { - auto traverseLayers = [&](const LayerVector::Visitor& visitor) { - bool stopLayerFound = false; - auto filterVisitor = [&](Layer* layer) { - // We don't want to capture any layers beyond the stop layer - if (stopLayerFound) return; - - if (!layerFilterFn(layer->getDebugName(), layer->getSequence(), - Rect(layer->getBounds()), layer->getTransform(), - stopLayerFound)) { - return; - } - visitor(layer); - }; - mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {}, - filterVisitor); - }; - getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); - } + auto filterFn = [&](const frontend::LayerSnapshot& snapshot, bool& outStopTraversal) -> bool { + const Rect bounds = frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds), + snapshot.transparentRegionHint); + const ui::Transform transform = snapshot.geomLayerTransform; + return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform, + outStopTraversal); + }; + auto getLayerSnapshotsFn = + mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, filterFn); std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() && @@ -372,17 +348,30 @@ void RegionSamplingThread::captureSample() { constexpr bool kGrayscale = false; constexpr bool kIsProtected = false; - if (const auto fenceResult = - mFlinger.captureScreenshot(SurfaceFlinger::RenderAreaBuilderVariant( - std::in_place_type<DisplayRenderAreaBuilder>, - sampledBounds, sampledBounds.getSize(), - ui::Dataspace::V0_SRGB, - kHintForSeamlessTransition, - true /* captureSecureLayers */, displayWeak), - getLayerSnapshots, buffer, kRegionSampling, kGrayscale, - kIsProtected, nullptr) + SurfaceFlinger::RenderAreaBuilderVariant + renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds, + sampledBounds.getSize(), ui::Dataspace::V0_SRGB, + kHintForSeamlessTransition, true /* captureSecureLayers */, + displayWeak); + + FenceResult fenceResult; + if (FlagManager::getInstance().single_hop_screenshot() && + FlagManager::getInstance().ce_fence_promise()) { + std::vector<sp<LayerFE>> layerFEs; + auto displayState = + mFlinger.getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, + getLayerSnapshotsFn, layerFEs); + fenceResult = + mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, + kIsProtected, nullptr, displayState, layerFEs) .get(); - fenceResult.ok()) { + } else { + fenceResult = + mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer, + kRegionSampling, kGrayscale, kIsProtected, nullptr) + .get(); + } + if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 85ce713703..dd3c4b074c 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -737,7 +737,9 @@ bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, F return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now); }; - Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns()); + Fps displayFps = !FlagManager::getInstance().vrr_bugfix_24q4() && mRenderRateOpt + ? *mRenderRateOpt + : Fps::fromPeriodNsecs(mIdealPeriod.ns()); const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate); const auto now = TimePoint::now(); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 5f81cd45cc..59345db67c 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -40,6 +40,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> +#include <com_android_graphics_surfaceflinger_flags.h> #include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> @@ -813,8 +814,24 @@ void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& bui .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); } else { const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK; +// TODO: b/341728634 - Clean up conditional compilation. +// Note: this guard in particular must check e.g. +// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE directly (instead of calling e.g. +// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE)) because that macro is undefined +// in the libsurfaceflingerflags_test variant of com_android_graphics_surfaceflinger_flags.h, which +// is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :) +#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \ + COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE const bool useGraphite = FlagManager::getInstance().graphite_renderengine() && renderengine::RenderEngine::canSupport(kVulkan); +#else + const bool useGraphite = false; + if (FlagManager::getInstance().graphite_renderengine()) { + ALOGE("RenderEngine's Graphite Skia backend was requested with the " + "debug.renderengine.graphite system property, but it is not compiled in this " + "build! Falling back to Ganesh backend selection logic."); + } +#endif const bool useVulkan = useGraphite || (FlagManager::getInstance().vulkan_renderengine() && renderengine::RenderEngine::canSupport(kVulkan)); @@ -7846,14 +7863,13 @@ status_t SurfaceFlinger::setSchedAttr(bool enabled) { namespace { -ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display, +ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, + const compositionengine::impl::OutputCompositionState& state, bool capturingHdrLayers, bool hintForSeamlessTransition) { - if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) { + if (requestedDataspace != ui::Dataspace::UNKNOWN) { return requestedDataspace; } - const auto& state = display->getCompositionDisplay()->getState(); - const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode); // TODO: Enable once HDR screenshots are ready. @@ -7933,23 +7949,14 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, } } - GetLayerSnapshotsFunction getLayerSnapshots; - if (mLayerLifecycleManagerEnabled) { - getLayerSnapshots = - getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds)); - } else { - auto traverseLayers = [this, args, excludeLayerIds, - layerStack](const LayerVector::Visitor& visitor) { - traverseLayersInLayerStack(layerStack, args.uid, std::move(excludeLayerIds), visitor); - }; - getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); - } + GetLayerSnapshotsFunction getLayerSnapshotsFn = + getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds)); captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>, args.sourceCrop, reqSize, args.dataspace, args.hintForSeamlessTransition, args.captureSecureLayers, displayWeak), - getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected, + getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected, args.grayscale, captureListener); } @@ -7986,16 +7993,9 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args return; } - GetLayerSnapshotsFunction getLayerSnapshots; - if (mLayerLifecycleManagerEnabled) { - getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, - /*snapshotFilterFn=*/nullptr); - } else { - auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) { - traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {}, visitor); - }; - getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); - } + GetLayerSnapshotsFunction getLayerSnapshotsFn = + getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, + /*snapshotFilterFn=*/nullptr); if (captureListener == nullptr) { ALOGE("capture screen must provide a capture listener callback"); @@ -8010,7 +8010,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args Rect(), size, args.dataspace, args.hintForSeamlessTransition, false /* captureSecureLayers */, displayWeak), - getLayerSnapshots, size, args.pixelFormat, kAllowProtected, kGrayscale, + getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale, captureListener); } @@ -8092,42 +8092,16 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, return; } - GetLayerSnapshotsFunction getLayerSnapshots; - if (mLayerLifecycleManagerEnabled) { - std::optional<FloatRect> parentCrop = std::nullopt; - if (args.childrenOnly) { - parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height) - : crop.toFloatRect(); - } - - getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid, - std::move(excludeLayerIds), - args.childrenOnly, parentCrop); - } else { - auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) { - parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (!layer->isVisible()) { - return; - } else if (args.childrenOnly && layer == parent.get()) { - return; - } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) { - return; - } - - auto p = sp<Layer>::fromExisting(layer); - while (p != nullptr) { - if (excludeLayerIds.count(p->sequence) != 0) { - return; - } - p = p->getParent(); - } - - visitor(layer); - }); - }; - getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); + std::optional<FloatRect> parentCrop = std::nullopt; + if (args.childrenOnly) { + parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height) + : crop.toFloatRect(); } + GetLayerSnapshotsFunction getLayerSnapshotsFn = + getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds), + args.childrenOnly, parentCrop); + if (captureListener == nullptr) { ALOGD("capture screen must provide a capture listener callback"); invokeScreenCaptureError(BAD_VALUE, captureListener); @@ -8138,7 +8112,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, reqSize, dataspace, args.captureSecureLayers, args.hintForSeamlessTransition, parent, args.childrenOnly), - getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected, + getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected, args.grayscale, captureListener); } @@ -8153,12 +8127,15 @@ void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* laye owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack); } -bool SurfaceFlinger::layersHasProtectedLayer( - const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { +// Loop over all visible layers to see whether there's any protected layer. A protected layer is +// typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. +// A protected layer has no implication on whether it's secure, which is explicitly set by +// application to avoid being screenshot or drawn via unsecure display. +bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const { bool protectedLayerFound = false; - for (auto& [_, layerFe] : layers) { + for (auto& layerFE : layers) { protectedLayerFound |= - (layerFe->mSnapshot->isVisible && layerFe->mSnapshot->hasProtectedContent); + (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent); if (protectedLayerFound) { break; } @@ -8166,8 +8143,28 @@ bool SurfaceFlinger::layersHasProtectedLayer( return protectedLayerFound; } +// Getting layer snapshots and display should take place on main thread. +// Accessing display requires mStateLock, and contention for this lock +// is reduced when grabbed from the main thread, thus also reducing +// risk of deadlocks. +std::optional<SurfaceFlinger::OutputCompositionState> +SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread( + RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, + std::vector<sp<LayerFE>>& layerFEs) { + return mScheduler + ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) { + auto layers = getLayerSnapshotsFn(); + for (auto& [layer, layerFE] : layers) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + } + layerFEs = extractLayerFEs(layers); + return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); + }) + .get(); +} + void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder, - GetLayerSnapshotsFunction getLayerSnapshots, + GetLayerSnapshotsFunction getLayerSnapshotsFn, ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>& captureListener) { @@ -8181,74 +8178,185 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil return; } - // Loop over all visible layers to see whether there's any protected layer. A protected layer is - // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. - // A protected layer has no implication on whether it's secure, which is explicitly set by - // application to avoid being screenshot or drawn via unsecure display. - const bool supportsProtected = getRenderEngine().supportsProtectedContent(); - bool hasProtectedLayer = false; - if (allowProtected && supportsProtected) { - auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get(); - hasProtectedLayer = layersHasProtectedLayer(layers); - } - const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; - const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - (isProtected ? GRALLOC_USAGE_PROTECTED - : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> buffer = - getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), - static_cast<android_pixel_format>(reqPixelFormat), - 1 /* layerCount */, usage, "screenshot"); - - const status_t bufferStatus = buffer->initCheck(); - if (bufferStatus != OK) { - // Animations may end up being really janky, but don't crash here. - // Otherwise an irreponsible process may cause an SF crash by allocating - // too much. - ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - invokeScreenCaptureError(bufferStatus, captureListener); - return; + if (FlagManager::getInstance().single_hop_screenshot() && + FlagManager::getInstance().ce_fence_promise()) { + std::vector<sp<LayerFE>> layerFEs; + auto displayState = + getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, + layerFEs); + + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); + bool hasProtectedLayer = false; + if (allowProtected && supportsProtected) { + hasProtectedLayer = layersHasProtectedLayer(layerFEs); + } + const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; + const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + (isProtected ? GRALLOC_USAGE_PROTECTED + : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + sp<GraphicBuffer> buffer = + getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1 /* layerCount */, usage, "screenshot"); + + const status_t bufferStatus = buffer->initCheck(); + if (bufferStatus != OK) { + // Animations may end up being really janky, but don't crash here. + // Otherwise an irreponsible process may cause an SF crash by allocating + // too much. + ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); + invokeScreenCaptureError(bufferStatus, captureListener); + return; + } + const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< + renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), + renderengine::impl::ExternalTexture::Usage:: + WRITEABLE); + auto futureFence = + captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, + isProtected, captureListener, displayState, layerFEs); + futureFence.get(); + + } else { + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); + bool hasProtectedLayer = false; + if (allowProtected && supportsProtected) { + auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get(); + hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers)); + } + const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; + const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + (isProtected ? GRALLOC_USAGE_PROTECTED + : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + sp<GraphicBuffer> buffer = + getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1 /* layerCount */, usage, "screenshot"); + + const status_t bufferStatus = buffer->initCheck(); + if (bufferStatus != OK) { + // Animations may end up being really janky, but don't crash here. + // Otherwise an irreponsible process may cause an SF crash by allocating + // too much. + ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); + invokeScreenCaptureError(bufferStatus, captureListener); + return; + } + const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< + renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), + renderengine::impl::ExternalTexture::Usage:: + WRITEABLE); + auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture, + false /* regionSampling */, grayscale, + isProtected, captureListener); + futureFence.get(); } - const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), - renderengine::impl::ExternalTexture::Usage:: - WRITEABLE); - auto futureFence = - captureScreenshot(renderAreaBuilder, getLayerSnapshots, texture, - false /* regionSampling */, grayscale, isProtected, captureListener); - futureFence.get(); } -ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( - RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshots, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) { - ATRACE_CALL(); - - auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( - kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { - // LayerSnapshots must be obtained from the main thread. - auto layers = getLayerSnapshots(); - +std::optional<SurfaceFlinger::OutputCompositionState> +SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& renderAreaBuilder) { + sp<const DisplayDevice> display = nullptr; + { + Mutex::Autolock lock(mStateLock); if (auto* layerRenderAreaBuilder = std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) { // LayerSnapshotBuilder should only be accessed from the main thread. - frontend::LayerSnapshot* snapshot = + const frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence()); if (!snapshot) { ALOGW("Couldn't find layer snapshot for %d", layerRenderAreaBuilder->layer->getSequence()); } else { layerRenderAreaBuilder->setLayerSnapshot(*snapshot); + display = findDisplay( + [layerStack = snapshot->outputFilter.layerStack](const auto& display) { + return display.getLayerStack() == layerStack; + }); } + } else if (auto* displayRenderAreaBuilder = + std::get_if<DisplayRenderAreaBuilder>(&renderAreaBuilder)) { + display = displayRenderAreaBuilder->displayWeak.promote(); + } + + if (display == nullptr) { + display = getDefaultDisplayDeviceLocked(); + } + + if (display != nullptr) { + return std::optional{display->getCompositionDisplay()->getState()}; } + } + return std::nullopt; +} + +std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs( + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { + std::vector<sp<LayerFE>> layerFEs; + layerFEs.reserve(layers.size()); + for (const auto& [_, layerFE] : layers) { + layerFEs.push_back(layerFE); + } + return layerFEs; +} + +ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( + const RenderAreaBuilderVariant& renderAreaBuilder, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, + bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) { + ATRACE_CALL(); + + ScreenCaptureResults captureResults; + std::unique_ptr<const RenderArea> renderArea = + std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); }, + renderAreaBuilder); + + if (!renderArea) { + ALOGW("Skipping screen capture because of invalid render area."); + if (captureListener) { + captureResults.fenceResult = base::unexpected(NO_MEMORY); + captureListener->onScreenCaptureCompleted(captureResults); + } + return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); + } + + // Empty vector needed to pass into renderScreenImpl for legacy path + std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers; + ftl::SharedFuture<FenceResult> renderFuture = + renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected, + captureResults, displayState, layers, layerFEs); + + if (captureListener) { + // Defer blocking on renderFuture back to the Binder thread. + return ftl::Future(std::move(renderFuture)) + .then([captureListener, captureResults = std::move(captureResults)]( + FenceResult fenceResult) mutable -> FenceResult { + captureResults.fenceResult = std::move(fenceResult); + captureListener->onScreenCaptureCompleted(captureResults); + return base::unexpected(NO_ERROR); + }) + .share(); + } + return renderFuture; +} + +ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( + RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, + bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) { + ATRACE_CALL(); + auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( + kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { + auto layers = getLayerSnapshotsFn(); if (FlagManager::getInstance().ce_fence_promise()) { for (auto& [layer, layerFE] : layers) { attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); } } + auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); ScreenCaptureResults captureResults; std::unique_ptr<const RenderArea> renderArea = @@ -8264,9 +8372,10 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); } + auto layerFEs = extractLayerFEs(layers); ftl::SharedFuture<FenceResult> renderFuture = renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, - isProtected, captureResults, layers); + isProtected, captureResults, displayState, layers, layerFEs); if (captureListener) { // Defer blocking on renderFuture back to the Binder thread. @@ -8296,22 +8405,14 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - std::unique_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) { - auto layers = getLayerSnapshots(); - return renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected, - captureResults, layers); -} - -ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( std::unique_ptr<const RenderArea> renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, - std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) { + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) { ATRACE_CALL(); - for (auto& [_, layerFE] : layers) { + for (auto& layerFE : layerFEs) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure); captureResults.capturedHdrLayers |= isHdrLayer(*snapshot); @@ -8334,78 +8435,54 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() && !renderArea->getHintForSeamlessTransition(); - { - Mutex::Autolock lock(mStateLock); - const DisplayDevice* display = nullptr; - if (parent) { - const frontend::LayerSnapshot* snapshot = - mLayerSnapshotBuilder.getSnapshot(parent->sequence); - if (snapshot) { - display = findDisplay([layerStack = snapshot->outputFilter.layerStack]( - const auto& display) { - return display.getLayerStack() == layerStack; - }).get(); - } - } - - if (display == nullptr) { - display = renderArea->getDisplayDevice().get(); - } + if (displayState) { + const auto& state = displayState.value(); + captureResults.capturedDataspace = + pickBestDataspace(requestedDataspace, state, captureResults.capturedHdrLayers, + renderArea->getHintForSeamlessTransition()); + sdrWhitePointNits = state.sdrWhitePointNits; - if (display == nullptr) { - display = getDefaultDisplayDeviceLocked().get(); - } - - if (display != nullptr) { - const auto& state = display->getCompositionDisplay()->getState(); - captureResults.capturedDataspace = - pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers, - renderArea->getHintForSeamlessTransition()); - sdrWhitePointNits = state.sdrWhitePointNits; - - if (!captureResults.capturedHdrLayers) { - displayBrightnessNits = sdrWhitePointNits; - } else { - displayBrightnessNits = state.displayBrightnessNits; - - if (!enableLocalTonemapping) { - // Only clamp the display brightness if this is not a seamless transition. - // Otherwise for seamless transitions it's important to match the current - // display state as the buffer will be shown under these same conditions, and we - // want to avoid any flickers - if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) { - // Restrict the amount of HDR "headroom" in the screenshot to avoid - // over-dimming the SDR portion. 2.0 chosen by experimentation - constexpr float kMaxScreenshotHeadroom = 2.0f; - displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, - displayBrightnessNits); - } + if (!captureResults.capturedHdrLayers) { + displayBrightnessNits = sdrWhitePointNits; + } else { + displayBrightnessNits = state.displayBrightnessNits; + if (!enableLocalTonemapping) { + // Only clamp the display brightness if this is not a seamless transition. + // Otherwise for seamless transitions it's important to match the current + // display state as the buffer will be shown under these same conditions, and we + // want to avoid any flickers + if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) { + // Restrict the amount of HDR "headroom" in the screenshot to avoid + // over-dimming the SDR portion. 2.0 chosen by experimentation + constexpr float kMaxScreenshotHeadroom = 2.0f; + displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, + displayBrightnessNits); } } + } - // Screenshots leaving the device should be colorimetric - if (requestedDataspace == ui::Dataspace::UNKNOWN && - renderArea->getHintForSeamlessTransition()) { - renderIntent = state.renderIntent; - } + // Screenshots leaving the device should be colorimetric + if (requestedDataspace == ui::Dataspace::UNKNOWN && + renderArea->getHintForSeamlessTransition()) { + renderIntent = state.renderIntent; } } captureResults.buffer = capturedBuffer->getBuffer(); ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK}; - if (!layers.empty()) { - const sp<LayerFE>& layerFE = layers.back().second; + if (!layerFEs.empty()) { + const sp<LayerFE>& layerFE = layerFEs.back(); layerStack = layerFE->getCompositionState()->outputFilter.layerStack; } - auto copyLayerFEs = [&layers]() { - std::vector<sp<compositionengine::LayerFE>> layerFEs; - layerFEs.reserve(layers.size()); - for (const auto& [_, layerFE] : layers) { - layerFEs.push_back(layerFE); + auto copyLayerFEs = [&layerFEs]() { + std::vector<sp<compositionengine::LayerFE>> ceLayerFEs; + ceLayerFEs.reserve(layerFEs.size()); + for (const auto& layerFE : layerFEs) { + ceLayerFEs.push_back(layerFE); } - return layerFEs; + return ceLayerFEs; }; auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, @@ -8474,8 +8551,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call // to CompositionEngine::present. - auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() - : ftl::yield(present()).share(); + ftl::SharedFuture<FenceResult> presentFuture; + if (FlagManager::getInstance().single_hop_screenshot() && + FlagManager::getInstance().ce_fence_promise()) { + presentFuture = mRenderEngine->isThreaded() + ? ftl::yield(present()).share() + : mScheduler->schedule(std::move(present)).share(); + } else { + presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() + : ftl::yield(present()).share(); + } if (!FlagManager::getInstance().ce_fence_promise()) { for (auto& [layer, layerFE] : layers) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8a390162d9..12307172f7 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -892,32 +892,46 @@ private: void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack); // Checks if a protected layer exists in a list of layers. - bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; + bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const; + + using OutputCompositionState = compositionengine::impl::OutputCompositionState; + + std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread( + RenderAreaBuilderVariant& renderAreaBuilder, + GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs); void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>&); + std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( + RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); + + // Legacy layer raw pointer is not safe to access outside the main thread. + // Creates a new vector consisting only of LayerFEs, which can be safely + // accessed outside the main thread. + std::vector<sp<LayerFE>> extractLayerFEs( + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; + ftl::SharedFuture<FenceResult> captureScreenshot( + const RenderAreaBuilderVariant& renderAreaBuilder, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, + bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + std::optional<OutputCompositionState>& displayState, + std::vector<sp<LayerFE>>& layerFEs); + + ftl::SharedFuture<FenceResult> captureScreenshotLegacy( RenderAreaBuilderVariant, GetLayerSnapshotsFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&); - // Overloaded version of renderScreenImpl that is used when layer snapshots have - // not yet been captured, and thus cannot yet be passed in as a parameter. - // Needed for TestableSurfaceFlinger. - ftl::SharedFuture<FenceResult> renderScreenImpl( - std::unique_ptr<const RenderArea>, GetLayerSnapshotsFunction, - const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock) - REQUIRES(kMainThreadContext); - ftl::SharedFuture<FenceResult> renderScreenImpl( std::unique_ptr<const RenderArea>, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&, - std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) EXCLUDES(mStateLock) - REQUIRES(kMainThreadContext); + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, + std::vector<sp<LayerFE>>& layerFEs); // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a // matching ownerUid diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index 6b971a7597..c3594bcf55 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -17,6 +17,7 @@ cc_defaults { shared_libs: [ "libSurfaceFlingerProp", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], static_libs: [ "librenderengine_includes", @@ -56,6 +57,7 @@ cc_defaults { name: "libsurfaceflinger_common_deps", shared_libs: [ "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], static_libs: [ "libsurfaceflinger_common", @@ -69,6 +71,7 @@ cc_defaults { name: "libsurfaceflinger_common_test_deps", shared_libs: [ "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], static_libs: [ "libsurfaceflinger_common_test", diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index b78809a59a..4216771515 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -149,6 +149,8 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(local_tonemap_screenshots); DUMP_READ_ONLY_FLAG(override_trusted_overlay); DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache); + DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine); + DUMP_READ_ONLY_FLAG(single_hop_screenshot); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -248,6 +250,8 @@ FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, ""); FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots"); FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, ""); FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, ""); +FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, ""); +FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, ""); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 302d311bb9..22118ab585 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -88,6 +88,8 @@ public: bool local_tonemap_screenshots() const; bool override_trusted_overlay() const; bool flush_buffer_slots_to_uncache() const; + bool force_compile_graphite_renderengine() const; + bool single_hop_screenshot() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index dea74d0de6..56bca7fb18 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -161,7 +161,7 @@ flag { flag { name: "graphite_renderengine" namespace: "core_graphics" - description: "Use Skia's Graphite Vulkan backend in RenderEngine." + description: "Compile AND enable Skia's Graphite Vulkan backend in RenderEngine. See also: force_compile_graphite_renderengine." bug: "293371537" is_fixed_read_only: true } diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index 07728912f1..f4d4ee978d 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -66,6 +66,14 @@ flag { } # flush_buffer_slots_to_uncache flag { + name: "force_compile_graphite_renderengine" + namespace: "core_graphics" + description: "Compile Skia's Graphite Vulkan backend in RenderEngine, but do NOT enable it, unless graphite_renderengine is also set. It can also be enabled with the debug.renderengine.graphite system property for testing. In contrast, the graphite_renderengine flag both compiles AND enables Graphite in RenderEngine." + bug: "293371537" + is_fixed_read_only: true +} # force_compile_graphite_renderengine + +flag { name: "frame_rate_category_mrr" namespace: "core_graphics" description: "Enable to use frame rate category and newer frame rate votes such as GTE in SurfaceFlinger scheduler, to guard dVRR changes from MRR devices" @@ -95,6 +103,17 @@ flag { is_fixed_read_only: true } # local_tonemap_screenshots +flag { + name: "single_hop_screenshot" + namespace: "window_surfaces" + description: "Only access SF main thread once during a screenshot" + bug: "285553970" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } + } # single_hop_screenshot + flag { name: "override_trusted_overlay" namespace: "window_surfaces" diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 0ddddbd7f3..08973de195 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -205,7 +205,8 @@ void CompositionTest::captureScreenComposition() { CaptureArgs::UNSET_UID, {}, visitor); }; - auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); + // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function + auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers); const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; @@ -215,7 +216,7 @@ void CompositionTest::captureScreenComposition() { HAL_PIXEL_FORMAT_RGBA_8888, 1, usage); - auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots, + auto future = mFlinger.renderScreenImpl(mDisplay, std::move(renderArea), getLayerSnapshotsFn, mCaptureScreenBuffer, regionSampling); ASSERT_TRUE(future.valid()); const auto fenceResult = future.get(); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp index 2b333f4b87..b79bdb4231 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -777,4 +777,28 @@ TEST_F(LayerHierarchyTest, canMirrorDisplayWithMirrors) { EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected); } +// (b/343901186) +TEST_F(LayerHierarchyTest, cleanUpDanglingMirrorLayer) { + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); + mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 2); + UPDATE_AND_VERIFY(hierarchyBuilder); + + std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 2, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); + + // destroy layer handle + reparentLayer(2, UNASSIGNED_LAYER_ID); + destroyLayerHandle(2); + UPDATE_AND_VERIFY(hierarchyBuilder); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 265f804fc8..8c72a7dba5 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -485,23 +485,28 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto renderScreenImpl(std::unique_ptr<const RenderArea> renderArea, - SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers, + auto renderScreenImpl(const sp<DisplayDevice> display, + std::unique_ptr<const RenderArea> renderArea, + SurfaceFlinger::GetLayerSnapshotsFunction getLayerSnapshotsFn, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling) { + Mutex::Autolock lock(mFlinger->mStateLock); + ftl::FakeGuard guard(kMainThreadContext); + ScreenCaptureResults captureResults; - return FTL_FAKE_GUARD(kMainThreadContext, - mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers, - buffer, regionSampling, - false /* grayscale */, - false /* isProtected */, captureResults)); + auto displayState = std::optional{display->getCompositionDisplay()->getState()}; + auto layers = getLayerSnapshotsFn(); + auto layerFEs = mFlinger->extractLayerFEs(layers); + + return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling, + false /* grayscale */, false /* isProtected */, + captureResults, displayState, layers, layerFEs); } auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid, std::unordered_set<uint32_t> excludeLayerIds, const LayerVector::Visitor& visitor) { - return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, - excludeLayerIds, visitor); + return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor); } auto getDisplayNativePrimaries(const sp<IBinder>& displayToken, diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index eafba0ad8d..5109ea6793 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -535,6 +535,28 @@ TEST_F(VSyncPredictorTest, isVSyncInPhase) { } } +TEST_F(VSyncPredictorTest, isVSyncInPhaseWithRenderRate) { + SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true); + auto last = mNow; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod; + last = mNow; + tracker.addVsyncTimestamp(mNow); + } + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + mPeriod), Eq(mNow + 2 * mPeriod)); + + const auto renderRateFps = Fps::fromPeriodNsecs(mPeriod * 2); + tracker.setRenderRate(renderRateFps, /*applyImmediately*/ true); + + EXPECT_FALSE(tracker.isVSyncInPhase(mNow, renderRateFps)); + EXPECT_TRUE(tracker.isVSyncInPhase(mNow + mPeriod, renderRateFps)); + EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 2 * mPeriod, renderRateFps)); + EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 3 * mPeriod, renderRateFps)); +} + TEST_F(VSyncPredictorTest, isVSyncInPhaseForDivisors) { auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 74d3d9dc64..9e67725b58 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1174,7 +1174,8 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( pSurfaceFormat); if (surfaceCompressionProps && - driver.GetPhysicalDeviceImageFormatProperties2KHR) { + (driver.GetPhysicalDeviceImageFormatProperties2KHR || + driver.GetPhysicalDeviceImageFormatProperties2)) { VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {}; imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; @@ -1205,7 +1206,7 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( imageFormatProps.pNext = &compressionProps; VkResult compressionRes = - driver.GetPhysicalDeviceImageFormatProperties2KHR( + GetPhysicalDeviceImageFormatProperties2( physicalDevice, &imageFormatInfo, &imageFormatProps); if (compressionRes == VK_SUCCESS) { |