Merge "FTL: Amend outdated ftl::Function example usage" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1b61e3a..6576ffd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2195,6 +2195,74 @@
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 @@
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 @@
break;
}
- const uint64_t start = Nanotime();
- const int ret = dump_backtrace_to_file_timeout(
- pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd);
+ 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;
+ }
- 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);
+ const uint64_t start = Nanotime();
+ 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;
+ }
+
+ 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 88801ca..0ec5abb 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -51,7 +51,7 @@
}
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 ae7d8e0..2d889fd 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,7 +50,27 @@
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 fec56f0..ee98d7a 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1002,7 +1002,6 @@
* 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 3ca9c19..ec08cdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -99,6 +99,18 @@
/* 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 @@
* 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 @@
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 @@
// 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 @@
// 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 0000000..457d474
--- /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 090e35b..cb1d114 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -28,6 +28,7 @@
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_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 @@
header_libs: [
"libbinder_headers_base",
],
+ export_header_lib_headers: [
+ "libbinder_headers_base",
+ ],
cflags: [
"-Wextra",
@@ -369,6 +491,7 @@
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 @@
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 @@
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 {
+ 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 @@
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 7a855e1..6594aa6 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 @@
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 @@
}
}
}
- [[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 ef96f80..fbc8125 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1027,6 +1027,7 @@
goto finish;
case BR_FROZEN_REPLY:
+ ALOGW("Transaction failed because process frozen.");
err = FAILED_TRANSACTION;
goto finish;
@@ -1578,8 +1579,8 @@
}
#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 5703eb7..04869a1 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -26,6 +26,7 @@
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 1eace85..893ee15 100644
--- a/libs/binder/OS_android.cpp
+++ b/libs/binder/OS_android.cpp
@@ -44,6 +44,10 @@
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 01f3fe0..0c64eb6 100644
--- a/libs/binder/OS_non_android_linux.cpp
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -39,6 +39,8 @@
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 1504715..5b157cc 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -82,13 +82,12 @@
} \
}
-#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 fb2781b..8485ecd 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -516,22 +516,23 @@
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 @@
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 @@
}
#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 e8d9829..40102bb 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -129,9 +129,9 @@
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 268157e..2f450cb 100644
--- a/libs/binder/include/binder/Trace.h
+++ b/libs/binder/include/binder/Trace.h
@@ -41,6 +41,7 @@
// 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 f2ca22f..2de6658 100644
--- a/libs/binder/liblog_stub/Android.bp
+++ b/libs/binder/liblog_stub/Android.bp
@@ -30,6 +30,7 @@
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 9a2d14a..26c228d 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -32,17 +32,11 @@
],
}
-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 @@
],
cflags: [
- "-DBINDER_WITH_KERNEL_IPC",
"-Wall",
"-Wextra",
"-Wextra-semi",
@@ -59,14 +52,48 @@
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 @@
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 52edae4..41b30a0 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 @@
* 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 5529455..4436dbe 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -49,7 +49,25 @@
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 2d1175b..f5b0071 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -114,7 +114,6 @@
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 bd24a20..3fe55d6 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -25,6 +25,7 @@
cc_defaults {
name: "binder_test_defaults",
+ cmake_snapshot_supported: true,
cflags: [
"-Wall",
"-Werror",
@@ -62,6 +63,7 @@
"binderStatusUnitTest.cpp",
"binderMemoryHeapBaseUnitTest.cpp",
"binderRecordedTransactionTest.cpp",
+ "binderPersistableBundleTest.cpp",
],
shared_libs: [
"libbinder",
@@ -141,6 +143,7 @@
name: "binderRpcTestIface",
vendor_available: true,
host_supported: true,
+ cmake_snapshot_supported: true,
unstable: true,
srcs: [
"BinderRpcTestClientInfo.aidl",
@@ -222,6 +225,7 @@
cc_defaults {
name: "binderRpcTest_common_defaults",
host_supported: true,
+ cmake_snapshot_supported: true,
target: {
darwin: {
enabled: false,
@@ -381,6 +385,9 @@
static_libs: [
"libbinder_rpc_single_threaded_no_kernel",
],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
}
cc_binary {
@@ -461,6 +468,20 @@
}
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 @@
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 9788d9d..00406ed 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::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 @@
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 @@
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 0000000..392e018
--- /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 c044d39..19882ea 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1278,7 +1278,7 @@
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 @@
.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 acc0373..dc22647 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 @@
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 e59dc82..91145f0 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 @@
[](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 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 @@
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, 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 09db326..157ab3c 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -40,6 +40,8 @@
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 ed17014..d782f42 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -113,6 +113,27 @@
"--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 @@
"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 ee121d5..b098147 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -96,6 +96,19 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
}
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 @@
// 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 abc0392..fcf490d 100644
--- a/libs/input/InputConsumer.cpp
+++ b/libs/input/InputConsumer.cpp
@@ -181,7 +181,8 @@
}
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 @@
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 @@
}
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 @@
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 bc67810..9333ab8 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -273,10 +273,7 @@
}
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 0000000..0c2c7be
--- /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 90ed2b7..a77dfa5 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -116,6 +116,31 @@
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 @@
* 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 560166c..a2192cb 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -150,3 +150,10 @@
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 d0dbd6f..0574245 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 @@
#[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 @@
}
}
+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 0000000..1063fac
--- /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 fb3f520..5010475 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 @@
/// }
/// ```
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 @@
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 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 476b5cf..3717f49 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 @@
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
};
+} // namespace
+
// --- PointerCoordsTest ---
class PointerCoordsTest : public BaseTest {
@@ -344,13 +362,15 @@
}
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 @@
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 @@
}
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 @@
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 70529bb..f49469c 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -96,7 +96,9 @@
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 48512f7..e65a919 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -89,7 +89,9 @@
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 2dc9fdb..8d8b530 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -297,10 +297,9 @@
}
/**
- * 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 @@
// 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 757d935..4a04467 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,6 +50,7 @@
"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 1c60563..bc3976d 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 fc16c56..5e756b0 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -197,7 +197,9 @@
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 @@
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 af0489a..f20b002 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 @@
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 0eea187..0783714 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -46,6 +46,7 @@
"libshaders",
"libtonemap",
"libsurfaceflinger_common",
+ "libsurfaceflingerflags",
],
header_libs: [
"libtonemap_headers",
@@ -64,5 +65,6 @@
"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 4dcaff9..a8a9823 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 @@
}
};
+// 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 @@
return renderengine::RenderEngine::SkiaBackend::GRAPHITE;
}
};
+#endif
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
public:
@@ -1497,10 +1509,15 @@
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 c511f4a..cbf3055 100644
--- a/libs/sensor/libsensor_flags.aconfig
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -8,3 +8,10 @@
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 02453ef..7d3a2df 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 @@
// --- 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 @@
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 @@
return;
}
mWindowInfoListener->onPointerChoreographerDestroyed();
+ mUnregisterListener(mWindowInfoListener);
}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
@@ -391,7 +413,7 @@
}
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 @@
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 11c5a0c..d9b075f 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -108,10 +108,6 @@
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 @@
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 @@
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 29aa3c3..1a0ec48 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -56,7 +56,9 @@
cc_defaults {
name: "libinputdispatcher_defaults",
- srcs: [":libinputdispatcher_sources"],
+ srcs: [
+ ":libinputdispatcher_sources",
+ ],
shared_libs: [
"libbase",
"libbinder",
@@ -78,6 +80,7 @@
"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 dae2b61..5ed5eb8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -444,10 +444,12 @@
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 @@
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 @@
*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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
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 6240e7f..e2fc7a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -687,8 +687,8 @@
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 2d7554c..0b17507 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -123,7 +123,8 @@
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 9b9633a..3d30ad6 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 @@
}
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 @@
}
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 @@
}
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 b807b27..2daf195 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -237,6 +237,12 @@
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
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 b9523ef..ab13ad4 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -102,6 +102,7 @@
mEventHub(eventHub),
mPolicy(policy),
mNextListener(listener),
+ mKeyboardClassifier(std::make_unique<KeyboardClassifier>()),
mGlobalMetaState(AMETA_NONE),
mLedMetaState(AMETA_NONE),
mGeneration(1),
@@ -1076,4 +1077,8 @@
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 27b9d23..49ad8b5 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -418,7 +418,11 @@
}
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 39d2f5b..7cf584d 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -85,64 +85,67 @@
/*
* 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 4c9af2e..2a7e262 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -79,6 +79,8 @@
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 @@
void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+ void setKeyboardType(KeyboardType keyboardType);
+
void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -196,6 +200,7 @@
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 @@
}
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 7e701c5..6f8c289 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -157,6 +157,7 @@
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 @@
// 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 907a49f..e0e0ac2 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 @@
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 09a5f08..90685de 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -155,8 +155,8 @@
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 c67314d..20cdb59 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -55,14 +55,14 @@
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 @@
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 75ca9c0..2108488 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -34,7 +34,7 @@
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 @@
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 987d2d0..3af1d04 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -61,13 +61,13 @@
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 97df02b..c040a7b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -39,7 +39,7 @@
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 c7eea0e..2c51448 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -78,7 +78,7 @@
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 5ce4d30..41e018d 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -259,29 +259,29 @@
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 @@
}
break;
default:
- newValue = rawEvent->value * axis.scale + axis.offset;
+ newValue = rawEvent.value * axis.scale + axis.offset;
highNewValue = 0.0f;
break;
}
@@ -300,9 +300,9 @@
}
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 313f092..621d38b 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -35,7 +35,7 @@
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 2124555..91ec62d 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 @@
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 @@
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::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 @@
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 @@
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 @@
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 f2d3f4d..c7df558 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -36,7 +36,7 @@
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 @@
};
uint32_t mSource{};
- int32_t mKeyboardType{};
std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
std::vector<KeyDown> mKeyDowns{}; // keys that are down
@@ -85,8 +84,7 @@
} 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 bca9d91..1986fe2 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -40,7 +40,7 @@
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 5c173f3..cca2327 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -31,7 +31,7 @@
~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 0ddbc06..27ff52f 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -101,12 +101,12 @@
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 fe5d152..14c540b 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -39,7 +39,7 @@
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 a131e35..d7f2993 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -251,35 +251,35 @@
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 a55dcd1..63bc151 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -40,7 +40,7 @@
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 ed0e270..140bb0c 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -30,7 +30,7 @@
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 7726bfb..bc38711 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -31,7 +31,7 @@
~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 05338da..f131fb7 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -30,16 +30,16 @@
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 2fb48bb..5d8aa2c 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -29,7 +29,7 @@
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 8b4b691..a383490 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -537,18 +537,6 @@
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 @@
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 @@
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 @@
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 b24f2ff..30c58a5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -174,7 +174,7 @@
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 b8911db..24efae8 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 @@
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 @@
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 546fa5b..8baa63e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -56,7 +56,7 @@
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 8d78d0f..a3a48ef 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -36,7 +36,7 @@
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 9079c73..7519682 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
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 153236c..9e722d4 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,32 +47,32 @@
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 6960644..256b2bb 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -29,7 +29,7 @@
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 0714694..f85cab2 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -39,14 +39,14 @@
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 ae1b7a3..e563620 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -31,7 +31,7 @@
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 b3f1700..4919068 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -45,12 +45,12 @@
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 @@
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 a0f2147..388ed82 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -76,7 +76,7 @@
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 27b8e40..2b82ddf 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -45,29 +45,29 @@
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 93056f0..fb74bca 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -28,7 +28,7 @@
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 8c4bed3..ba8577e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -52,60 +52,60 @@
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 e829692..c7adf84 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -33,7 +33,7 @@
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 b89b7f3..6885adb 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -40,15 +40,15 @@
}
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 633448e..07e62c6 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -44,7 +44,7 @@
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 69264f8..f4a5e0d 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -90,7 +90,7 @@
// 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 255c7eb..5b7cc2d 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -47,6 +47,7 @@
"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 6df339e..8b44af3 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -31,6 +31,7 @@
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 @@
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 @@
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 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 @@
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 6c2277c..6c7c7fb 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -23,6 +23,7 @@
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_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 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
});
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 530416c..e17ee3a 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -215,6 +215,10 @@
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 @@
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 2c86146..62ff10f 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -115,6 +115,7 @@
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 @@
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 456013e..d0998ba 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -77,10 +77,12 @@
}
void FakePointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) {
+ mDisplaysToSkipScreenshotFlagChanged = true;
mDisplaysToSkipScreenshot.insert(displayId);
}
void FakePointerController::clearSkipScreenshotFlags() {
+ mDisplaysToSkipScreenshotFlagChanged = true;
mDisplaysToSkipScreenshot.clear();
}
@@ -125,13 +127,21 @@
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 8d95f65..2c76c62 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -57,7 +57,10 @@
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 @@
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 ff9bd9e..34c81fc 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -81,7 +81,7 @@
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 @@
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 8de28c6..56a05a3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -610,28 +610,6 @@
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 @@
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,41 +6629,20 @@
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) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
- ui::LogicalDisplayId::DEFAULT);
-
- window->setFocusable(true);
- mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- setFocusedWindow(window);
-
- window->consumeFocusEvent(true);
-
- mDispatcher->notifyKey(
- generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
- mDispatcher->waitForIdle();
-
- // System key is not passed down
- window->assertNoEvents();
-
- // Should have poked user activity
- mFakePolicy->assertUserActivityPoked();
-}
-
-TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) {
+TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserActivity) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
@@ -6697,8 +6655,10 @@
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
@@ -6708,6 +6668,39 @@
mFakePolicy->assertUserActivityPoked();
}
+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(GetParam());
+
+ window->setFocusable(true);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_A)
+ .policyFlags(0)
+ .build());
+ mDispatcher->waitForIdle();
+
+ // Key is not passed down
+ window->assertNoEvents();
+
+ // 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 @@
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 fea1349..b5c9232 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -104,7 +104,7 @@
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 @@
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 8536ff0..fe238f3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -309,9 +309,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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, 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, 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, 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, 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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, 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 @@
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, 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 @@
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);
+ }
};
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) {
+// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::EXTERNAL);
+ }
+};
+
+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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,24 @@
ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
}
-TEST_F(LightControllerTest, PlayerIdLight) {
+TEST_F(LightControllerTest, SonyPlayerIdLight) {
RawLightInfo info1 = {.id = 1,
- .name = "player1",
+ .name = "sony1",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info2 = {.id = 2,
- .name = "player2",
+ .name = "sony2",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info3 = {.id = 3,
- .name = "player3",
+ .name = "sony3",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info4 = {.id = 4,
- .name = "player4",
+ .name = "sony4",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
@@ -10626,6 +10617,49 @@
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 = "player-1",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info2 = {.id = 2,
+ .name = "player-2",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info3 = {.id = 3,
+ .name = "player-3",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info4 = {.id = 4,
+ .name = "player-4",
+ .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("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 32acb5f..a9d370a 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::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 @@
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;
}
- it++;
+
+ 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);
+ }
}
}
return std::tuple{std::move(tracedMotions), std::move(tracedKeys),
diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h
index ed20bc8..bda5521 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 617d67f..2ccd93e 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 6389cdc..4441724 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 @@
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 @@
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 031b77d..ada841d 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -67,8 +67,7 @@
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 5e67506..b441a23 100644
--- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -38,7 +38,7 @@
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 69a7382..3f2d6ec 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 @@
// --- 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 @@
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 @@
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 @@
"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 @@
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 af20a27..8361517 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -81,7 +81,7 @@
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 922cbdf..9e02502 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -50,11 +50,10 @@
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 @@
},
[&]() -> 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 25f2f2e..ff425dd 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -338,9 +338,11 @@
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 d3f6690..f29577d 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -100,7 +100,7 @@
},
[&]() -> 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 ac2030a..a42d447 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -44,7 +44,7 @@
[&]() -> 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 643e8b9..c620032 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -176,7 +176,7 @@
},
[&]() -> 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 afaf0ae..f4b0265 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -84,6 +84,7 @@
"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 90c2330..7347ac7 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 5b499a8..7abfbaa 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -27,4 +27,4 @@
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 8ca796e..1b6c598 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -85,6 +85,7 @@
"libui",
"libutils",
"libSurfaceFlingerProp",
+ "libaconfig_storage_read_api_cc"
],
static_libs: [
"iinputflinger_aidl_lib_static",
@@ -98,6 +99,7 @@
"libscheduler",
"libserviceutils",
"libshaders",
+ "libsurfaceflingerflags",
"libtimestats",
"libtonemap",
],
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 38cf053..a57e626 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -474,7 +474,6 @@
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 @@
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 2b20de3..39a6b77 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -260,27 +260,36 @@
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::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ std::vector<LayerHierarchy*> hierarchiesToAttach = getDescendants(root);
+ for (LayerHierarchy* hierarchy : hierarchiesToAttach) {
+ attachToRelativeParent(hierarchy);
}
}
void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
- if (root->mLayer) {
- detachFromRelativeParent(root);
- }
- for (auto& [child, childVariant] : root->mChildren) {
- if (childVariant == LayerHierarchy::Variant::Detached ||
- childVariant == LayerHierarchy::Variant::Attached) {
- detachHierarchyFromRelativeParent(child);
- }
+ 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 69710be..d023f9e 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -218,6 +218,7 @@
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 4b0618e..dd5e8bd 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -152,6 +152,10 @@
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 b40f332..9527a99 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -200,19 +200,13 @@
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 @@
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 93ec36e..b2896f0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -65,7 +65,7 @@
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 2b4e234..5add290 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -315,39 +315,15 @@
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 @@
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 85ce713..dd3c4b0 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -737,7 +737,9 @@
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 5f81cd4..59345db 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 @@
.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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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");
+ if (FlagManager::getInstance().single_hop_screenshot() &&
+ FlagManager::getInstance().ce_fence_promise()) {
+ std::vector<sp<LayerFE>> layerFEs;
+ auto displayState =
+ getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn,
+ layerFEs);
- 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 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 @@
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::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 @@
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 (displayState) {
+ const auto& state = displayState.value();
+ captureResults.capturedDataspace =
+ pickBestDataspace(requestedDataspace, state, captureResults.capturedHdrLayers,
+ renderArea->getHintForSeamlessTransition());
+ sdrWhitePointNits = state.sdrWhitePointNits;
- if (display == nullptr) {
- display = renderArea->getDisplayDevice().get();
- }
-
- 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 @@
//
// 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 8a39016..1230717 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -892,32 +892,46 @@
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 6b971a7..c3594bc 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -17,6 +17,7 @@
shared_libs: [
"libSurfaceFlingerProp",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"librenderengine_includes",
@@ -56,6 +57,7 @@
name: "libsurfaceflinger_common_deps",
shared_libs: [
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"libsurfaceflinger_common",
@@ -69,6 +71,7 @@
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 b78809a..4216771 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -149,6 +149,8 @@
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(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 302d311..22118ab 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -88,6 +88,8 @@
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 dea74d0..56bca7f 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -161,7 +161,7 @@
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 0772891..f4d4ee9 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -66,6 +66,14 @@
} # 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 @@
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 0ddddbd..08973de 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -205,7 +205,8 @@
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 @@
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 2b333f4..b79bdb4 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -777,4 +777,28 @@
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 265f804..8c72a7d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -485,23 +485,28 @@
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 eafba0a..5109ea6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -535,6 +535,28 @@
}
}
+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 74d3d9d..9e67725 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1174,7 +1174,8 @@
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 @@
imageFormatProps.pNext = &compressionProps;
VkResult compressionRes =
- driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ GetPhysicalDeviceImageFormatProperties2(
physicalDevice, &imageFormatInfo,
&imageFormatProps);
if (compressionRes == VK_SUCCESS) {