Merge "Use dimming ratio when detecting hdr type for local TMO" into main
diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp
index 27ef788..cf27e19 100644
--- a/cmds/cmd/Android.bp
+++ b/cmds/cmd/Android.bp
@@ -27,14 +27,10 @@
"libselinux",
"libbinder",
],
- whole_static_libs: [
- "libc++fs",
- ],
cflags: [
"-Wall",
"-Werror",
- "-DXP_UNIX",
],
}
@@ -58,6 +54,5 @@
cflags: [
"-Wall",
"-Werror",
- "-DXP_UNIX",
],
}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a1c10f5..b22cc2a 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -159,7 +159,6 @@
"tests/dumpstate_test.cpp",
],
static_libs: [
- "libc++fs",
"libgmock",
],
test_config: "dumpstate_test.xml",
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..9384926 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,11 +50,36 @@
exit 1
fi
-if pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
- # Handled by Pre-reboot Dexopt.
- exit 0
+# 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
+}
+
+PR_DEXOPT_JOB_VERSION="$(pm art pr-dexopt-job --version)"
+if (( $? == 0 )) && (( $PR_DEXOPT_JOB_VERSION >= 2 )); then
+ # 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
+ echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt."
+else
+ echo "Pre-reboot Dexopt is too old. Fall back to otapreopt."
fi
-echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt."
if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
# We require an updated chroot wrapper that reads dexopt commands from stdin.
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 61fe316..f3e024c 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -102,7 +102,6 @@
"libziparchive",
"liblog",
"liblogwrap",
- "libc++fs",
],
product_variables: {
arc: {
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index 21ac11b..00fd249 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -27,7 +27,6 @@
],
cflags: [
- "-DXP_UNIX",
"-Wall",
"-Werror",
],
@@ -46,7 +45,6 @@
],
cflags: [
- "-DXP_UNIX",
"-DVENDORSERVICES",
"-Wall",
"-Werror",
@@ -65,7 +63,6 @@
],
cflags: [
- "-DXP_UNIX",
"-Wall",
"-Werror",
],
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 3897197..e5d7b74 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -29,6 +29,7 @@
"liblog",
"libutils",
"libselinux",
+ "libperfetto_c",
],
target: {
@@ -48,7 +49,13 @@
enabled: false,
},
vendor: {
- exclude_shared_libs: ["libvintf"],
+ exclude_shared_libs: [
+ "libvintf",
+ "libperfetto_c",
+ ],
+ },
+ recovery: {
+ exclude_shared_libs: ["libperfetto_c"],
},
},
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 95a05cd..1333599 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -27,6 +28,11 @@
#include <cutils/multiuser.h>
#include <thread>
+#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
#ifndef VENDORSERVICEMANAGER
#include <vintf/VintfObject.h>
#ifdef __ANDROID_RECOVERY__
@@ -42,6 +48,17 @@
namespace android {
+#if defined(VENDORSERVICEMANAGER) || defined(__ANDROID_RECOVERY__)
+#define SM_PERFETTO_TRACE_FUNC(...)
+#else
+
+PERFETTO_TE_CATEGORIES_DEFINE(PERFETTO_SM_CATEGORIES);
+
+#define SM_PERFETTO_TRACE_FUNC(...) \
+ PERFETTO_TE_SCOPED(servicemanager, PERFETTO_TE_SLICE_BEGIN(__func__) __VA_OPT__(, ) __VA_ARGS__)
+
+#endif // !(defined(VENDORSERVICEMANAGER) || defined(__ANDROID_RECOVERY__))
+
bool is_multiuser_uid_isolated(uid_t uid) {
uid_t appid = multiuser_get_app_id(uid);
return appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
@@ -348,18 +365,24 @@
}
Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
*outBinder = tryGetService(name, true);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
*outBinder = tryGetService(name, false);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
@@ -398,6 +421,8 @@
}
bool isValidServiceName(const std::string& name) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
if (name.size() == 0) return false;
if (name.size() > 127) return false;
@@ -413,6 +438,8 @@
}
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
@@ -505,6 +532,8 @@
}
Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
+ SM_PERFETTO_TRACE_FUNC();
+
if (!mAccess->canList(mAccess->getCallingContext())) {
return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
@@ -532,6 +561,8 @@
Status ServiceManager::registerForNotifications(
const std::string& name, const sp<IServiceCallback>& callback) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
@@ -578,6 +609,8 @@
}
Status ServiceManager::unregisterForNotifications(
const std::string& name, const sp<IServiceCallback>& callback) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
@@ -601,6 +634,8 @@
}
Status ServiceManager::isDeclared(const std::string& name, bool* outReturn) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
@@ -616,6 +651,8 @@
}
binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("interface", interface.c_str()));
+
auto ctx = mAccess->getCallingContext();
std::vector<std::string> allInstances;
@@ -640,6 +677,8 @@
Status ServiceManager::updatableViaApex(const std::string& name,
std::optional<std::string>* outReturn) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
@@ -656,6 +695,8 @@
Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& apexName,
std::vector<std::string>* outReturn) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("apexName", apexName.c_str()));
+
auto ctx = mAccess->getCallingContext();
std::vector<std::string> apexUpdatableNames;
@@ -674,12 +715,13 @@
if (outReturn->size() == 0 && apexUpdatableNames.size() != 0) {
return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
-
return Status::ok();
}
Status ServiceManager::getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
@@ -697,6 +739,8 @@
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
+ SM_PERFETTO_TRACE_FUNC();
+
std::vector<sp<IServiceCallback>>& listeners = (*it)->second;
for (auto lit = listeners.begin(); lit != listeners.end();) {
@@ -716,6 +760,8 @@
}
void ServiceManager::binderDied(const wp<IBinder>& who) {
+ SM_PERFETTO_TRACE_FUNC();
+
for (auto it = mNameToService.begin(); it != mNameToService.end();) {
if (who == it->second.binder) {
// TODO: currently, this entry contains the state also
@@ -758,6 +804,8 @@
Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
if (cb == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Callback null.");
}
@@ -918,6 +966,8 @@
}
Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+
if (binder == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null service.");
}
@@ -983,6 +1033,7 @@
}
Status ServiceManager::getServiceDebugInfo(std::vector<ServiceDebugInfo>* outReturn) {
+ SM_PERFETTO_TRACE_FUNC();
if (!mAccess->canList(mAccess->getCallingContext())) {
return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
}
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 3b925a4..1536014 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -20,6 +20,10 @@
#include <android/os/IClientCallback.h>
#include <android/os/IServiceCallback.h>
+#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+#include "perfetto/public/te_category_macros.h"
+#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
#include "Access.h"
namespace android {
@@ -29,6 +33,11 @@
using os::IServiceCallback;
using os::ServiceDebugInfo;
+#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+#define PERFETTO_SM_CATEGORIES(C) C(servicemanager, "servicemanager", "Service Manager category")
+PERFETTO_TE_CATEGORIES_DECLARE(PERFETTO_SM_CATEGORIES);
+#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
public:
ServiceManager(std::unique_ptr<Access>&& access);
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 07908ba..c126e91 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -26,6 +26,26 @@
#include "Access.h"
#include "ServiceManager.h"
+#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
+#include <perfetto/public/producer.h>
+#include <perfetto/public/te_category_macros.h>
+#include <perfetto/public/te_macros.h>
+#include <perfetto/public/track_event.h>
+
+namespace android {
+
+static void register_perfetto_te_categories() {
+ struct PerfettoProducerInitArgs perfetto_args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
+ perfetto_args.backends = PERFETTO_BACKEND_SYSTEM;
+ PerfettoProducerInit(perfetto_args);
+ PerfettoTeInit();
+ PERFETTO_TE_REGISTER_CATEGORIES(PERFETTO_SM_CATEGORIES);
+}
+} // namespace android
+
+#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
using ::android::Access;
using ::android::IPCThreadState;
using ::android::Looper;
@@ -132,6 +152,10 @@
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
+#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+ android::register_perfetto_te_categories();
+#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+
LOG(INFO) << "Starting sm instance on " << driver;
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 0300f8c..766c768 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -418,6 +418,14 @@
defaults: ["frameworks_native_data_etc_defaults"],
}
+// installed in system
+prebuilt_etc {
+ name: "android.software.preview_sdk.prebuilt.xml",
+ relative_install_path: "permissions",
+ src: "android.software.preview_sdk.xml",
+ filename_from_src: true,
+}
+
prebuilt_etc {
name: "android.software.sip.voip.prebuilt.xml",
src: "android.software.sip.voip.xml",
@@ -460,6 +468,22 @@
defaults: ["frameworks_native_data_etc_defaults"],
}
+// installed in system
+prebuilt_etc {
+ name: "android.software.webview.prebuilt.xml",
+ relative_install_path: "permissions",
+ src: "android.software.webview.xml",
+ filename_from_src: true,
+}
+
+// installed in system
+prebuilt_etc {
+ name: "android.software.window_magnification.prebuilt.xml",
+ relative_install_path: "permissions",
+ src: "android.software.window_magnification.xml",
+ filename_from_src: true,
+}
+
prebuilt_etc {
name: "aosp_excluded_hardware.prebuilt.xml",
src: "aosp_excluded_hardware.xml",
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
new file mode 100644
index 0000000..945e720
--- /dev/null
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!-- This is the standard set of features for devices to support Telephony Satellite API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.satellite" />
+</permissions>
\ No newline at end of file
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/android/system_fonts.h b/include/android/system_fonts.h
index 94484ea..2d3a214 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -31,27 +31,27 @@
*
* \code{.cpp}
* ASystemFontIterator* iterator = ASystemFontIterator_open();
- * ASystemFont* font = NULL;
+ * AFont* font = NULL;
*
* while ((font = ASystemFontIterator_next(iterator)) != nullptr) {
* // Look if the font is your desired one.
- * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font)
- * && ASystemFont_getLocale(font) == NULL) {
+ * if (AFont_getWeight(font) == 400 && !AFont_isItalic(font)
+ * && AFont_getLocale(font) == NULL) {
* break;
* }
- * ASystemFont_close(font);
+ * AFont_close(font);
* }
* ASystemFontIterator_close(iterator);
*
- * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY);
- * int collectionIndex = ASystemFont_getCollectionINdex(font);
+ * int fd = open(AFont_getFontFilePath(font), O_RDONLY | O_CLOEXEC);
+ * int collectionIndex = AFont_getCollectionIndex(font);
* std::vector<std::pair<uint32_t, float>> variationSettings;
- * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) {
+ * for (size_t i = 0; i < AFont_getAxisCount(font); ++i) {
* variationSettings.push_back(std::make_pair(
- * ASystemFont_getAxisTag(font, i),
- * ASystemFont_getAxisValue(font, i)));
+ * AFont_getAxisTag(font, i),
+ * AFont_getAxisValue(font, i)));
* }
- * ASystemFont_close(font);
+ * AFont_close(font);
*
* // Use this font for your text rendering engine.
*
@@ -99,7 +99,7 @@
/**
* Create a system font iterator.
*
- * Use ASystemFont_close() to close the iterator.
+ * Use ASystemFontIterator_close() to close the iterator.
*
* Available since API level 29.
*
@@ -123,7 +123,7 @@
*
* \param iterator an iterator for the system fonts. Passing NULL is not allowed.
* \return a font. If no more font is available, returns nullptr. You need to release the returned
- * font by ASystemFont_close when it is no longer needed.
+ * font with AFont_close() when it is no longer needed.
*/
AFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29);
diff --git a/include/ftl/expected.h b/include/ftl/expected.h
index 57448dc..7e765c5 100644
--- a/include/ftl/expected.h
+++ b/include/ftl/expected.h
@@ -69,6 +69,36 @@
exp_.value(); \
})
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short),
+// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E.
+// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not
+// need to propagate R because T is not relevant to the caller.
+//
+// Example usage:
+//
+// using StringExp = ftl::Expected<std::string, std::errc>;
+//
+// std::errc repeat(StringExp exp, std::string& out) {
+// const std::string str = FTL_EXPECT(exp);
+// out = str + str;
+// return std::errc::operation_in_progress;
+// }
+//
+// std::string str;
+// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str));
+// assert("haha"s == str);
+// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str));
+// assert("haha"s == str);
+//
+#define FTL_EXPECT(expr) \
+ ({ \
+ auto exp_ = (expr); \
+ if (!exp_.has_value()) { \
+ return std::move(exp_.error()); \
+ } \
+ exp_.value(); \
+ })
+
namespace android::ftl {
// Superset of base::expected<T, E> with monadic operations.
diff --git a/include/ftl/function.h b/include/ftl/function.h
index 3538ca4..bda5b75 100644
--- a/include/ftl/function.h
+++ b/include/ftl/function.h
@@ -123,7 +123,7 @@
// // Create a typedef to give a more meaningful name and bound the size.
// using MyFunction = ftl::Function<int(std::string_view), 2>;
// int* ptr = nullptr;
-// auto f1 = MyFunction::make_function(
+// auto f1 = MyFunction::make(
// [cls = &cls, ptr](std::string_view sv) {
// return cls->on_string(ptr, sv);
// });
diff --git a/include/input/Input.h b/include/input/Input.h
index 3ca9c19..456977b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -25,6 +25,7 @@
#include <android/input.h>
#ifdef __linux__
#include <android/os/IInputConstants.h>
+#include <android/os/MotionEventFlag.h>
#endif
#include <android/os/PointerIconType.h>
#include <math.h>
@@ -69,15 +70,17 @@
* actual intent.
*/
AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
- android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ static_cast<int32_t>(android::os::MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED),
+
AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING =
- android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING,
+ static_cast<int32_t>(android::os::MotionEventFlag::HOVER_EXIT_PENDING),
+
/**
* This flag indicates that the event has been generated by a gesture generator. It
* provides a hint to the GestureDetector to not apply any touch slop.
*/
AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE =
- android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE,
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_GENERATED_GESTURE),
/**
* This flag indicates that the event will not cause a focus change if it is directed to an
@@ -86,19 +89,31 @@
* into focus.
*/
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE =
- android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
+ static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE),
/**
* This event was generated or modified by accessibility service.
*/
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT),
AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS =
- android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS,
+ static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS),
/* Motion event is inconsistent with previously sent motion events. */
- AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
+ AMOTION_EVENT_FLAG_TAINTED = static_cast<int32_t>(android::os::MotionEventFlag::TAINTED),
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ static_cast<int32_t>(android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION),
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = static_cast<int32_t>(
+ android::os::MotionEventFlag::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 +224,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 +277,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 +491,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 +957,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/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index ebc74fb..78896ed 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -365,7 +365,7 @@
if (event.events & EPOLLIN) {
int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
if (rc == -1) {
- LOG(FATAL) << "adbd_auth: failed to read from framework fd";
+ PLOG(FATAL) << "adbd_auth: failed to read from framework fd";
} else if (rc == 0) {
LOG(INFO) << "adbd_auth: hit EOF on framework fd";
std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 090e35b..bd6a08e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -19,6 +19,7 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_native_license"],
+ default_team: "trendy_team_virtualization",
}
cc_library_headers {
@@ -28,6 +29,7 @@
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
+ cmake_snapshot_supported: true,
header_libs: [
"libbinder_headers_platform_shared",
@@ -83,6 +85,132 @@
},
}
+cc_cmake_snapshot {
+ name: "binder_sdk",
+ modules: [
+ "libbinder_sdk",
+ "libbinder_sdk_single_threaded",
+ "libbinder_ndk_sdk",
+ "googletest_cmake",
+
+ "binderRpcTestNoKernel",
+ "binderRpcTestSingleThreadedNoKernel",
+ "binderRpcWireProtocolTest",
+ ],
+ 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_pregenerated: "external/googletest",
+ },
+ {
+ android_name: "libgtest_main",
+ mapped_name: "GTest::gtest_main",
+ package_pregenerated: "external/googletest",
+ },
+ {
+ android_name: "googletest_cmake",
+ package_pregenerated: "external/googletest",
+ },
+
+ // 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 +254,9 @@
header_libs: [
"libbinder_headers_base",
],
+ export_header_lib_headers: [
+ "libbinder_headers_base",
+ ],
cflags: [
"-Wextra",
@@ -137,6 +268,21 @@
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
+
+ target: {
+ bionic: {
+ // Hide symbols by default and set the BUILDING_LIBBINDER macro so that
+ // the code knows to export them.
+ //
+ // Only enabled on bionic builds, where RTTI is disabled, because
+ // it is failing to export required typeinfo symbols.
+ // TODO: b/341341056 - Find a solution for non-bionic builds.
+ cflags: [
+ "-fvisibility=hidden",
+ "-DBUILDING_LIBBINDER",
+ ],
+ },
+ },
}
cc_defaults {
@@ -369,6 +515,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 +555,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 +568,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 +600,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 +702,7 @@
defaults: ["libbinder_tls_shared_deps"],
vendor_available: true,
host_supported: true,
+ cmake_snapshot_supported: true,
header_libs: [
"libbinder_headers",
@@ -745,7 +913,4 @@
"libutils",
"android.debug_aidl-cpp",
],
- static_libs: [
- "libc++fs",
- ],
}
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..c3bbdba 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -613,16 +613,19 @@
void IPCThreadState::blockUntilThreadAvailable()
{
- pthread_mutex_lock(&mProcess->mThreadCountLock);
- mProcess->mWaitingForThreads++;
- while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) {
- ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n",
- static_cast<unsigned long>(mProcess->mExecutingThreadsCount),
- static_cast<unsigned long>(mProcess->mMaxThreads));
- pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock);
- }
- mProcess->mWaitingForThreads--;
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
+ std::unique_lock lock_guard_(mProcess->mOnThreadAvailableLock);
+ mProcess->mOnThreadAvailableWaiting++;
+ mProcess->mOnThreadAvailableCondVar.wait(lock_guard_, [&] {
+ size_t max = mProcess->mMaxThreads;
+ size_t cur = mProcess->mExecutingThreadsCount;
+ if (cur < max) {
+ return true;
+ }
+ ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%zu mMaxThreads=%zu\n", cur,
+ max);
+ return false;
+ });
+ mProcess->mOnThreadAvailableWaiting--;
}
status_t IPCThreadState::getAndExecuteCommand()
@@ -642,34 +645,33 @@
ALOGI("%s", message.c_str());
}
- pthread_mutex_lock(&mProcess->mThreadCountLock);
- mProcess->mExecutingThreadsCount++;
- if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
- mProcess->mStarvationStartTimeMs == 0) {
- mProcess->mStarvationStartTimeMs = uptimeMillis();
+ size_t newThreadsCount = mProcess->mExecutingThreadsCount.fetch_add(1) + 1;
+ if (newThreadsCount >= mProcess->mMaxThreads) {
+ int64_t expected = 0;
+ mProcess->mStarvationStartTimeMs.compare_exchange_strong(expected, uptimeMillis());
}
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
result = executeCommand(cmd);
- pthread_mutex_lock(&mProcess->mThreadCountLock);
- mProcess->mExecutingThreadsCount--;
- if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
- mProcess->mStarvationStartTimeMs != 0) {
- int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
- if (starvationTimeMs > 100) {
- ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
- mProcess->mMaxThreads, starvationTimeMs);
+ size_t maxThreads = mProcess->mMaxThreads;
+ newThreadsCount = mProcess->mExecutingThreadsCount.fetch_sub(1) - 1;
+ if (newThreadsCount < maxThreads) {
+ size_t starvationStartTimeMs = mProcess->mStarvationStartTimeMs.exchange(0);
+ if (starvationStartTimeMs != 0) {
+ int64_t starvationTimeMs = uptimeMillis() - starvationStartTimeMs;
+ if (starvationTimeMs > 100) {
+ ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms", maxThreads,
+ starvationTimeMs);
+ }
}
- mProcess->mStarvationStartTimeMs = 0;
}
// Cond broadcast can be expensive, so don't send it every time a binder
// call is processed. b/168806193
- if (mProcess->mWaitingForThreads > 0) {
- pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
+ if (mProcess->mOnThreadAvailableWaiting > 0) {
+ std::lock_guard lock_guard_(mProcess->mOnThreadAvailableLock);
+ mProcess->mOnThreadAvailableCondVar.notify_all();
}
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
return result;
@@ -727,10 +729,9 @@
void IPCThreadState::joinThreadPool(bool isMain)
{
- LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
- pthread_mutex_lock(&mProcess->mThreadCountLock);
+ LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(),
+ getpid());
mProcess->mCurrentThreads++;
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
mIsLooper = true;
@@ -758,13 +759,11 @@
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
talkWithDriver(false);
- pthread_mutex_lock(&mProcess->mThreadCountLock);
- LOG_ALWAYS_FATAL_IF(mProcess->mCurrentThreads == 0,
- "Threadpool thread count = 0. Thread cannot exist and exit in empty "
- "threadpool\n"
+ size_t oldCount = mProcess->mCurrentThreads.fetch_sub(1);
+ LOG_ALWAYS_FATAL_IF(oldCount == 0,
+ "Threadpool thread count underflowed. Thread cannot exist and exit in "
+ "empty threadpool\n"
"Misconfiguration. Increase threadpool max threads configuration\n");
- mProcess->mCurrentThreads--;
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
status_t IPCThreadState::setupPolling(int* fd)
@@ -776,9 +775,7 @@
mOut.writeInt32(BC_ENTER_LOOPER);
flushCommands();
*fd = mProcess->mDriverFD;
- pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mCurrentThreads++;
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
return 0;
}
@@ -1027,6 +1024,7 @@
goto finish;
case BR_FROZEN_REPLY:
+ ALOGW("Transaction failed because process frozen.");
err = FAILED_TRANSACTION;
goto finish;
@@ -1578,8 +1576,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..ad5a6b3 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -407,9 +407,7 @@
ALOGV("Spawning new pooled thread, name=%s\n", name.c_str());
sp<Thread> t = sp<PoolThread>::make(isMain);
t->run(name.c_str());
- pthread_mutex_lock(&mThreadCountLock);
mKernelStartedThreads++;
- pthread_mutex_unlock(&mThreadCountLock);
}
// TODO: if startThreadPool is called on another thread after the process
// starts up, the kernel might think that it already requested those
@@ -432,19 +430,19 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
- pthread_mutex_lock(&mThreadCountLock);
- auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); });
-
if (mThreadPoolStarted) {
- LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1,
- "too many kernel-started threads: %zu > %zu + 1", mKernelStartedThreads,
- mMaxThreads);
+ size_t kernelStarted = mKernelStartedThreads;
+ size_t max = mMaxThreads;
+ size_t current = mCurrentThreads;
+
+ LOG_ALWAYS_FATAL_IF(kernelStarted > max + 1,
+ "too many kernel-started threads: %zu > %zu + 1", kernelStarted, max);
// calling startThreadPool starts a thread
size_t threads = 1;
// the kernel is configured to start up to mMaxThreads more threads
- threads += mMaxThreads;
+ threads += max;
// Users may call IPCThreadState::joinThreadPool directly. We don't
// currently have a way to count this directly (it could be added by
@@ -454,8 +452,8 @@
// in IPCThreadState, temporarily forget about the extra join threads.
// This is okay, because most callers of this method only care about
// having 0, 1, or more threads.
- if (mCurrentThreads > mKernelStartedThreads) {
- threads += mCurrentThreads - mKernelStartedThreads;
+ if (current > kernelStarted) {
+ threads += current - kernelStarted;
}
return threads;
@@ -463,10 +461,9 @@
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
- LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0,
- "Expecting 0 kernel started threads but have"
- " %zu",
- mKernelStartedThreads);
+ size_t kernelStarted = mKernelStartedThreads;
+ LOG_ALWAYS_FATAL_IF(kernelStarted != 0, "Expecting 0 kernel started threads but have %zu",
+ kernelStarted);
return mCurrentThreads;
}
@@ -516,22 +513,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;
@@ -553,10 +551,7 @@
: mDriverName(String8(driver)),
mDriverFD(-1),
mVMStart(MAP_FAILED),
- mThreadCountLock(PTHREAD_MUTEX_INITIALIZER),
- mThreadCountDecrement(PTHREAD_COND_INITIALIZER),
mExecutingThreadsCount(0),
- mWaitingForThreads(0),
mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
mCurrentThreads(0),
mKernelStartedThreads(0),
@@ -565,7 +560,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 +576,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/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index a466638..11898a0 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -23,6 +23,7 @@
#include <pthread.h>
+#include <atomic>
#include <mutex>
// ---------------------------------------------------------------------------
@@ -162,22 +163,21 @@
int mDriverFD;
void* mVMStart;
- // Protects thread count and wait variables below.
- mutable pthread_mutex_t mThreadCountLock;
- // Broadcast whenever mWaitingForThreads > 0
- pthread_cond_t mThreadCountDecrement;
+ mutable std::mutex mOnThreadAvailableLock;
+ std::condition_variable mOnThreadAvailableCondVar;
+ // Number of threads waiting on `mOnThreadAvailableCondVar`.
+ std::atomic_int64_t mOnThreadAvailableWaiting = 0;
+
// Number of binder threads current executing a command.
- size_t mExecutingThreadsCount;
- // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
- size_t mWaitingForThreads;
+ std::atomic_size_t mExecutingThreadsCount;
// Maximum number of lazy threads to be started in the threadpool by the kernel.
- size_t mMaxThreads;
+ std::atomic_size_t mMaxThreads;
// Current number of threads inside the thread pool.
- size_t mCurrentThreads;
+ std::atomic_size_t mCurrentThreads;
// Current number of pooled threads inside the thread pool.
- size_t mKernelStartedThreads;
+ std::atomic_size_t mKernelStartedThreads;
// Time when thread pool was emptied
- int64_t mStarvationStartTimeMs;
+ std::atomic_int64_t mStarvationStartTimeMs;
mutable std::mutex mLock; // protects everything below.
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_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index d570eab..cf7dc1a 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -26,13 +26,10 @@
#if defined(__ANDROID_VENDOR__)
#include <android/llndk-versioning.h>
#else // __ANDROID_VENDOR__
-#if defined(API_LEVEL_AT_LEAST)
-// Redefine API_LEVEL_AT_LEAST here to replace the version to __ANDROID_API_FUTURE__ as a workaround
-#undef API_LEVEL_AT_LEAST
-#endif
-// TODO(b/322384429) switch this __ANDROID_API_FUTURE__ to sdk_api_level when V is finalized
+#if !defined(API_LEVEL_AT_LEAST)
#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android __ANDROID_API_FUTURE__, *))
+ (__builtin_available(android sdk_api_level, *))
+#endif
#endif // __ANDROID_VENDOR__
namespace aidl::android::os {
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..4c7684c 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,
@@ -263,6 +267,7 @@
defaults: [
"binderRpcTest_common_defaults",
],
+ compile_multilib: "first",
srcs: [
"binderRpcTest.cpp",
@@ -332,7 +337,7 @@
],
}
-cc_test {
+cc_binary {
// The module name cannot start with "binderRpcTest" because
// then atest tries to execute it as part of binderRpcTest
name: "binder_rpc_test_service",
@@ -343,7 +348,7 @@
],
}
-cc_test {
+cc_binary {
name: "binder_rpc_test_service_no_kernel",
defaults: [
"binderRpcTest_service_defaults",
@@ -354,7 +359,7 @@
],
}
-cc_test {
+cc_binary {
name: "binder_rpc_test_service_single_threaded",
defaults: [
"binderRpcTest_service_defaults",
@@ -369,7 +374,7 @@
],
}
-cc_test {
+cc_binary {
name: "binder_rpc_test_service_single_threaded_no_kernel",
defaults: [
"binderRpcTest_service_defaults",
@@ -381,6 +386,9 @@
static_libs: [
"libbinder_rpc_single_threaded_no_kernel",
],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
}
cc_binary {
@@ -461,6 +469,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 +509,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/tests/binder_sdk/Android.bp b/libs/binder/tests/binder_sdk/Android.bp
new file mode 100644
index 0000000..4e884ad
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/Android.bp
@@ -0,0 +1,84 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+sh_test_host {
+ name: "binder_sdk_test",
+ src: "binder_sdk_test.sh",
+ test_suites: ["general-tests"],
+ test_options: {
+ unit_test: false,
+ },
+
+ data: [
+ ":binder_sdk",
+ ":cmake_root",
+ ],
+ data_bins: [
+ "cmake",
+ "ctest",
+ ],
+}
+
+sh_test_host {
+ name: "binder_sdk_docker_test_gcc",
+ src: "binder_sdk_docker_test.sh",
+ test_suites: ["general-tests"],
+ test_options: {
+ unit_test: false,
+ },
+
+ data: [
+ ":binder_sdk",
+ "gcc.Dockerfile",
+ ],
+}
+
+sh_test_host {
+ name: "binder_sdk_docker_test_clang",
+ src: "binder_sdk_docker_test.sh",
+ test_suites: ["general-tests"],
+ test_options: {
+ unit_test: false,
+ },
+
+ data: [
+ ":binder_sdk",
+ "clang.Dockerfile",
+ ],
+}
+
+sh_test_host {
+ name: "binder_sdk_docker_test_gnumake",
+ src: "binder_sdk_docker_test.sh",
+ test_suites: ["general-tests"],
+ test_options: {
+ unit_test: false,
+ },
+
+ data: [
+ ":binder_sdk",
+ "gnumake.Dockerfile",
+ ],
+}
diff --git a/libs/binder/tests/binder_sdk/binder_sdk_docker_test.sh b/libs/binder/tests/binder_sdk/binder_sdk_docker_test.sh
new file mode 100755
index 0000000..0eca846
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/binder_sdk_docker_test.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+set -ex
+
+TEST_NAME="$(basename "$0")"
+DOCKER_TAG="${TEST_NAME}-${RANDOM}${RANDOM}"
+DOCKER_FILE=*.Dockerfile
+DOCKER_RUN_FLAGS=
+
+# Guess if we're running as an Android test or directly
+if [ "$(ls -1 ${DOCKER_FILE} | wc -l)" == "1" ]; then
+ # likely running as `atest binder_sdk_docker_test_XYZ`
+ DOCKER_PATH="$(dirname $(readlink --canonicalize --no-newline binder_sdk.zip))"
+else
+ # likely running directly as `./binder_sdk_docker_test.sh` - provide mode for easy testing
+ RED='\033[1;31m'
+ NO_COLOR='\033[0m'
+
+ if ! modinfo vsock_loopback &>/dev/null ; then
+ echo -e "${RED}Module vsock_loopback is not installed.${NO_COLOR}"
+ exit 1
+ fi
+ if modprobe --dry-run --first-time vsock_loopback &>/dev/null ; then
+ echo "Module vsock_loopback is not loaded. Attempting to load..."
+ if ! sudo modprobe vsock_loopback ; then
+ echo -e "${RED}Module vsock_loopback is not loaded and attempt to load failed.${NO_COLOR}"
+ exit 1
+ fi
+ fi
+
+ DOCKER_RUN_FLAGS="--interactive --tty"
+
+ DOCKER_FILE="$1"
+ if [ ! -f "${DOCKER_FILE}" ]; then
+ echo -e "${RED}Docker file '${DOCKER_FILE}' doesn't exist. Please provide one as an argument.${NO_COLOR}"
+ exit 1
+ fi
+
+ if [ ! -d "${ANDROID_BUILD_TOP}" ]; then
+ echo -e "${RED}ANDROID_BUILD_TOP doesn't exist. Please lunch some target.${NO_COLOR}"
+ exit 1
+ fi
+ ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode binder_sdk
+ BINDER_SDK_ZIP="${ANDROID_BUILD_TOP}/out/soong/.intermediates/frameworks/native/libs/binder/binder_sdk/linux_glibc_x86_64/*/binder_sdk.zip"
+ DOCKER_PATH="$(dirname $(ls -1 ${BINDER_SDK_ZIP} | head --lines=1))"
+fi
+
+function cleanup {
+ docker rmi --force "${DOCKER_TAG}" 2>/dev/null || true
+}
+trap cleanup EXIT
+
+docker build --force-rm --tag "${DOCKER_TAG}" --file ${DOCKER_FILE} ${DOCKER_PATH}
+docker run ${DOCKER_RUN_FLAGS} --rm "${DOCKER_TAG}"
diff --git a/libs/binder/tests/binder_sdk/binder_sdk_test.sh b/libs/binder/tests/binder_sdk/binder_sdk_test.sh
new file mode 100644
index 0000000..1881ace
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/binder_sdk_test.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+set -ex
+
+RED='\033[1;31m'
+NO_COLOR='\033[0m'
+
+if [ ! -f "binder_sdk.zip" ]; then
+ echo -e "${RED}binder_sdk.zip doesn't exist. Are you running this test through 'atest binder_sdk_test'?${NO_COLOR}"
+ exit 1
+fi
+
+mkdir -p bin
+cp `pwd`/cmake bin/cmake
+cp `pwd`/ctest bin/ctest
+export PATH="`pwd`/bin:$PATH"
+
+WORKDIR=workdir_$RANDOM$RANDOM$RANDOM
+unzip -q -d $WORKDIR binder_sdk.zip
+cd $WORKDIR
+
+cmake .
+make -j
+make test ARGS="--parallel 32 --output-on-failure"
diff --git a/libs/binder/tests/binder_sdk/clang.Dockerfile b/libs/binder/tests/binder_sdk/clang.Dockerfile
new file mode 100644
index 0000000..aa1fec2
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/clang.Dockerfile
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+FROM debian:bookworm
+
+RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list && \
+ apt-get update -y && \
+ apt-get install -y clang cmake ninja-build unzip
+
+ADD binder_sdk.zip /
+RUN unzip -q -d binder_sdk binder_sdk.zip
+
+WORKDIR /binder_sdk
+RUN CC=clang CXX=clang++ cmake -G Ninja -B build .
+RUN cmake --build build
+
+WORKDIR /binder_sdk/build
+# Alternatively: `ninja test`, but it won't pass parallel argument
+ENTRYPOINT [ "ctest", "--parallel", "32", "--output-on-failure" ]
diff --git a/libs/binder/tests/binder_sdk/gcc.Dockerfile b/libs/binder/tests/binder_sdk/gcc.Dockerfile
new file mode 100644
index 0000000..fb2ee2c
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/gcc.Dockerfile
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+FROM gcc:9
+
+RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list && \
+ apt-get update -y && \
+ apt-get install -y cmake ninja-build
+
+ADD binder_sdk.zip /
+RUN unzip -q -d binder_sdk binder_sdk.zip
+
+WORKDIR /binder_sdk
+RUN CC=gcc CXX=g++ cmake -G Ninja -B build .
+RUN cmake --build build
+
+WORKDIR /binder_sdk/build
+# Alternatively: `ninja test`, but it won't pass parallel argument
+ENTRYPOINT [ "ctest", "--parallel", "32", "--output-on-failure" ]
diff --git a/libs/binder/tests/binder_sdk/gnumake.Dockerfile b/libs/binder/tests/binder_sdk/gnumake.Dockerfile
new file mode 100644
index 0000000..abe12fb
--- /dev/null
+++ b/libs/binder/tests/binder_sdk/gnumake.Dockerfile
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+FROM gcc:9
+
+RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list && \
+ apt-get update -y && \
+ apt-get install -y cmake
+
+ADD binder_sdk.zip /
+RUN unzip -q -d binder_sdk binder_sdk.zip
+
+WORKDIR /binder_sdk
+RUN cmake .
+RUN make -j
+
+ENTRYPOINT make test ARGS="--parallel 32 --output-on-failure"
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/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
index e46ccfb..975f689 100644
--- a/libs/binder/trusty/binderRpcTest/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -21,7 +21,6 @@
MANIFEST := $(LOCAL_DIR)/manifest.json
MODULE_SRCS += \
- $(FMTLIB_DIR)/src/format.cc \
$(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
diff --git a/libs/binder/trusty/binderRpcTest/service/rules.mk b/libs/binder/trusty/binderRpcTest/service/rules.mk
index 50ae3d2..5d1a51d 100644
--- a/libs/binder/trusty/binderRpcTest/service/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/service/rules.mk
@@ -21,7 +21,6 @@
MANIFEST := $(LOCAL_DIR)/manifest.json
MODULE_SRCS := \
- $(FMTLIB_DIR)/src/format.cc \
$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 5cbe0af..7caa48c 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -22,7 +22,6 @@
LIBBASE_DIR := system/libbase
LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
LIBUTILS_BINDER_DIR := system/core/libutils/binder
-FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
$(LOCAL_DIR)/../OS.cpp \
@@ -59,7 +58,6 @@
$(LIBBINDER_DIR)/ndk/include_cpp \
$(LIBBASE_DIR)/include \
$(LIBUTILS_BINDER_DIR)/include \
- $(FMTLIB_DIR)/include \
GLOBAL_COMPILEFLAGS += \
-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index f2f140d..5e38ad0 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -22,7 +22,6 @@
LIBBASE_DIR := system/libbase
LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
LIBUTILS_BINDER_DIR := system/core/libutils/binder
-FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
$(LOCAL_DIR)/OS.cpp \
@@ -59,7 +58,6 @@
$(LIBBINDER_DIR)/include \
$(LIBBASE_DIR)/include \
$(LIBUTILS_BINDER_DIR)/include \
- $(FMTLIB_DIR)/include \
# The android/binder_to_string.h header is shared between libbinder and
# libbinder_ndk and included by auto-generated AIDL C++ files
diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp
index 9b7f017..d5b1d7e 100644
--- a/libs/ftl/expected_test.cpp
+++ b/libs/ftl/expected_test.cpp
@@ -79,16 +79,28 @@
namespace {
-IntExp increment(IntExp exp) {
+IntExp increment_try(IntExp exp) {
const int i = FTL_TRY(exp);
return IntExp(i + 1);
}
-StringExp repeat(StringExp exp) {
+std::errc increment_expect(IntExp exp, int& out) {
+ const int i = FTL_EXPECT(exp);
+ out = i + 1;
+ return std::errc::operation_in_progress;
+}
+
+StringExp repeat_try(StringExp exp) {
const std::string str = FTL_TRY(exp);
return StringExp(str + str);
}
+std::errc repeat_expect(StringExp exp, std::string& out) {
+ const std::string str = FTL_EXPECT(exp);
+ out = str + str;
+ return std::errc::operation_in_progress;
+}
+
void uppercase(char& c, ftl::Optional<char> opt) {
c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
}
@@ -97,13 +109,13 @@
// Keep in sync with example usage in header file.
TEST(Expected, Try) {
- EXPECT_EQ(IntExp(100), increment(IntExp(99)));
- EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
+ EXPECT_EQ(IntExp(100), increment_try(IntExp(99)));
+ EXPECT_TRUE(increment_try(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
return e == std::errc::value_too_large;
}));
- EXPECT_EQ(StringExp("haha"s), repeat(StringExp("ha"s)));
- EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+ EXPECT_EQ(StringExp("haha"s), repeat_try(StringExp("ha"s)));
+ EXPECT_TRUE(repeat_try(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
return e == std::errc::bad_message;
}));
@@ -115,4 +127,19 @@
EXPECT_EQ(c, 'A');
}
+TEST(Expected, Expect) {
+ int i = 0;
+ EXPECT_EQ(std::errc::operation_in_progress, increment_expect(IntExp(99), i));
+ EXPECT_EQ(100, i);
+ EXPECT_EQ(std::errc::value_too_large,
+ increment_expect(ftl::Unexpected(std::errc::value_too_large), i));
+ EXPECT_EQ(100, i);
+
+ std::string str;
+ EXPECT_EQ(std::errc::operation_in_progress, repeat_expect(StringExp("ha"s), str));
+ EXPECT_EQ("haha"s, str);
+ EXPECT_EQ(std::errc::bad_message, repeat_expect(ftl::Unexpected(std::errc::bad_message), str));
+ EXPECT_EQ("haha"s, str);
+}
+
} // namespace android::test
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 2547297..9ef0eac 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -41,6 +41,11 @@
aconfig_declarations: "libgui_flags",
}
+cc_aconfig_library {
+ name: "libguiflags_no_apex",
+ aconfig_declarations: "libgui_flags",
+}
+
cc_library_headers {
name: "libgui_headers",
vendor_available: true,
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index f317a2e..739c3c2 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -613,8 +613,19 @@
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+
+ nsecs_t dequeueTime = -1;
+ {
+ std::lock_guard _lock{mTimestampMutex};
+ auto dequeueTimeIt = mDequeueTimestamps.find(buffer->getId());
+ if (dequeueTimeIt != mDequeueTimestamps.end()) {
+ dequeueTime = dequeueTimeIt->second;
+ mDequeueTimestamps.erase(dequeueTimeIt);
+ }
+ }
+
t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, mProducerId,
- releaseBufferCallback);
+ releaseBufferCallback, dequeueTime);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -658,17 +669,6 @@
mPendingFrameTimelines.pop();
}
- {
- std::lock_guard _lock{mTimestampMutex};
- auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
- if (dequeueTime != mDequeueTimestamps.end()) {
- Parcel p;
- p.writeInt64(dequeueTime->second);
- t->setMetadata(mSurfaceControl, gui::METADATA_DEQUEUE_TIME, p);
- mDequeueTimestamps.erase(dequeueTime);
- }
- }
-
mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
// All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index f3de96d..c46f9c5 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "DisplayEventDispatcher"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cinttypes>
#include <cstdint>
@@ -23,10 +24,13 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
-
#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <com_android_graphics_libgui_flags.h>
namespace android {
+using namespace com::android::graphics::libgui;
// Number of events to read at a time from the DisplayEventDispatcher pipe.
// The value should be large enough that we can quickly drain the pipe
@@ -171,6 +175,13 @@
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
*outVsyncEventData = ev.vsync.vsyncData;
+
+ // Trace the RenderRate for this app
+ if (ATRACE_ENABLED() && flags::trace_frame_rate_override()) {
+ const auto frameInterval = ev.vsync.vsyncData.frameInterval;
+ int fps = frameInterval > 0 ? 1e9f / frameInterval : 0;
+ ATRACE_INT("RenderRate", fps);
+ }
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
if (ev.hotplug.connectionError == 0) {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ff6b558..2699368 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -62,7 +62,7 @@
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index c82bde9..3745805 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -985,6 +985,7 @@
SAFE_PARCEL(output->writeBool, hasBarrier);
SAFE_PARCEL(output->writeUint64, barrierFrameNumber);
SAFE_PARCEL(output->writeUint32, producerId);
+ SAFE_PARCEL(output->writeInt64, dequeueTime);
return NO_ERROR;
}
@@ -1024,6 +1025,7 @@
SAFE_PARCEL(input->readBool, &hasBarrier);
SAFE_PARCEL(input->readUint64, &barrierFrameNumber);
SAFE_PARCEL(input->readUint32, &producerId);
+ SAFE_PARCEL(input->readInt64, &dequeueTime);
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b420aab..5db5394 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1059,7 +1059,8 @@
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
Vector<ComposerState> composerStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ Vector<DisplayState> displayStates;
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
true, {uncacheBuffer}, false, {}, generateId(), {});
@@ -1702,7 +1703,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& optFrameNumber,
- uint32_t producerId, ReleaseBufferCallback callback) {
+ uint32_t producerId, ReleaseBufferCallback callback, nsecs_t dequeueTime) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1718,6 +1719,7 @@
bufferData->frameNumber = frameNumber;
bufferData->producerId = producerId;
bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
+ bufferData->dequeueTime = dequeueTime;
if (fence) {
bufferData->acquireFence = *fence;
bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index eb4a802..1ecc216 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -112,7 +112,7 @@
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 9cf62bc..7ee291d 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -74,6 +74,7 @@
} // namespace android::gui
using android::gui::METADATA_ACCESSIBILITY_ID;
+using android::gui::METADATA_CALLING_UID;
using android::gui::METADATA_DEQUEUE_TIME;
using android::gui::METADATA_GAME_MODE;
using android::gui::METADATA_MOUSE_CURSOR;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 82889ef..5f2f8dc 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -128,6 +128,8 @@
client_cache_t cachedBuffer;
+ nsecs_t dequeueTime;
+
// Generates the release callback id based on the buffer id and frame number.
// This is used as an identifier when release callbacks are invoked.
ReleaseCallbackId generateReleaseCallbackId() const;
@@ -276,9 +278,9 @@
layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
- static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged |
- layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged |
- layer_state_t::eLayerStackChanged;
+ static constexpr uint64_t INPUT_CHANGES = layer_state_t::eAlphaChanged |
+ layer_state_t::eInputInfoChanged | layer_state_t::eDropInputModeChanged |
+ layer_state_t::eTrustedOverlayChanged | layer_state_t::eLayerStackChanged;
// Changes that affect the visible region on a display.
static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES |
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9712907..0862e03 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -568,7 +568,8 @@
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence = std::nullopt,
const std::optional<uint64_t>& frameNumber = std::nullopt,
- uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr);
+ uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr,
+ nsecs_t dequeueTime = -1);
Transaction& unsetBuffer(const sp<SurfaceControl>& sc);
std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index a902a8c..792966f 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -7,7 +7,7 @@
description: "This flag controls plumbing setFrameRate thru BufferQueue"
bug: "281695725"
is_fixed_read_only: true
-}
+} # bq_setframerate
flag {
name: "frametimestamps_previousrelease"
@@ -15,7 +15,7 @@
description: "Controls a fence fixup for timestamp apis"
bug: "310927247"
is_fixed_read_only: true
-}
+} # frametimestamps_previousrelease
flag {
name: "bq_extendedallocate"
@@ -23,4 +23,15 @@
description: "Add BQ support for allocate with extended options"
bug: "268382490"
is_fixed_read_only: true
-}
+} # bq_extendedallocate
+
+flag {
+ name: "trace_frame_rate_override"
+ namespace: "core_graphics"
+ description: "Trace FrameRateOverride fps"
+ bug: "347314033"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # trace_frame_rate_override
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 43cd0f8..5e91088 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -636,7 +636,7 @@
status_t setTransactionState(
const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index ed17014..c2a7ebb 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
"android/os/InputEventInjectionResult.aidl",
"android/os/InputEventInjectionSync.aidl",
"android/os/InputConfig.aidl",
+ "android/os/MotionEventFlag.aidl",
"android/os/PointerIconType.aidl",
],
}
@@ -113,6 +114,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 +226,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..e23fc94 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -49,105 +49,24 @@
const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
/**
- * This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it and the event directly passed through
- * the obscured area.
- *
- * A security sensitive application can check this flag to identify situations in which
- * a malicious application may have covered up part of its content for the purpose
- * of misleading the user or hijacking touches. An appropriate response might be
- * to drop the suspect touches or to take additional precautions to confirm the user's
- * actual intent.
- */
- const int MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1;
-
- /**
- * This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it and the event did not directly pass
- * through the obscured area.
- *
- * A security sensitive application can check this flag to identify situations in which
- * a malicious application may have covered up part of its content for the purpose
- * of misleading the user or hijacking touches. An appropriate response might be
- * to drop the suspect touches or to take additional precautions to confirm the user's
- * actual intent.
- *
- * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is
- * obstructed in areas other than the touched location.
- */
- const int MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
-
- /**
- * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
- * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
- * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
- * @hide
- */
- const int MOTION_EVENT_FLAG_HOVER_EXIT_PENDING = 0x4;
-
- /**
- * This flag indicates that the event has been generated by a gesture generator. It
- * provides a hint to the GestureDetector to not apply any touch slop.
- *
- * @hide
- */
- const int MOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8;
-
- /**
- * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
- * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
- * is set, the typical actions that occur in response for a pointer going up (such as click
- * handlers, end of drawing) should be aborted. This flag is typically set when the user was
- * accidentally touching the screen, such as by gripping the device, or placing the palm on the
- * screen.
- *
- * @see #ACTION_POINTER_UP
- * @see #ACTION_CANCEL
+ * Common input event flag used for both motion and key events for a gesture or pointer being
+ * canceled.
*/
const int INPUT_EVENT_FLAG_CANCELED = 0x20;
/**
- * This flag indicates that the event will not cause a focus change if it is directed to an
- * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer
- * gestures to allow the user to direct gestures to an unfocused window without bringing the
- * window into focus.
- * @hide
- */
- const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40;
-
- /**
- * 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.
+ * Common input event flag used for both motion and key events, indicating that the event
+ * was generated or modified by accessibility service.
*/
const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
/**
- * Private flag that indicates when the system has detected that this motion event
- * may be inconsistent with respect to the sequence of previously delivered motion events,
- * such as when a pointer move event is sent but the pointer is not down.
- *
- * @hide
- * @see #isTainted
- * @see #setTainted
+ * Common input event flag used for both motion and key events, indicating that the system has
+ * detected this event may be inconsistent with the current event sequence or gesture, such as
+ * when a pointer move event is sent but the pointer is not down.
*/
const int INPUT_EVENT_FLAG_TAINTED = 0x80000000;
- /**
- * Private flag indicating that this event was synthesized by the system and should be delivered
- * to the accessibility focused view first. When being dispatched such an event is not handled
- * by predecessors of the accessibility focused view and after the event reaches that view the
- * flag is cleared and normal event dispatch is performed. This ensures that the platform can
- * click on any view that has accessibility focus which is semantically equivalent to asking the
- * view to perform a click accessibility action but more generic as views not implementing click
- * action correctly can still be activated.
- *
- * @hide
- * @see #isTargetAccessibilityFocus()
- * @see #setTargetAccessibilityFocus(boolean)
- */
- const int MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
-
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
@@ -234,4 +153,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/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl
new file mode 100644
index 0000000..2093b06
--- /dev/null
+++ b/libs/input/android/os/MotionEventFlag.aidl
@@ -0,0 +1,152 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+import android.os.IInputConstants;
+
+/**
+ * Flag definitions for MotionEvents.
+ * @hide
+ */
+@Backing(type="int")
+enum MotionEventFlag {
+
+ /**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event directly passed through
+ * the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ */
+ WINDOW_IS_OBSCURED = 0x1,
+
+ /**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event did not directly pass
+ * through the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ *
+ * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is
+ * obstructed in areas other than the touched location.
+ */
+ WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
+
+ /**
+ * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
+ * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
+ * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
+ * @hide
+ */
+ HOVER_EXIT_PENDING = 0x4,
+
+ /**
+ * This flag indicates that the event has been generated by a gesture generator. It
+ * provides a hint to the GestureDetector to not apply any touch slop.
+ *
+ * @hide
+ */
+ IS_GENERATED_GESTURE = 0x8,
+
+ /**
+ * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+ * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+ * is set, the typical actions that occur in response for a pointer going up (such as click
+ * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+ * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+ * screen.
+ *
+ * @see #ACTION_POINTER_UP
+ * @see #ACTION_CANCEL
+ */
+ CANCELED = IInputConstants.INPUT_EVENT_FLAG_CANCELED,
+
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing the
+ * window into focus.
+ * @hide
+ */
+ 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
+ */
+ 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
+ */
+ 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.
+ */
+ IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+
+ /**
+ * Private flag that indicates when the system has detected that this motion event
+ * may be inconsistent with respect to the sequence of previously delivered motion events,
+ * such as when a pointer move event is sent but the pointer is not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ TAINTED = IInputConstants.INPUT_EVENT_FLAG_TAINTED,
+
+ /**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ TARGET_ACCESSIBILITY_FOCUS = 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..c46b7bb 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -16,22 +16,27 @@
//! 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 inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag;
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,
@@ -189,26 +194,34 @@
bitflags! {
/// MotionEvent flags.
+ /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum.
+ /// The flag values are redefined here as a bitflags API.
#[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 = MotionEventFlag::WINDOW_IS_OBSCURED.0 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 = MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED.0 as u32;
/// FLAG_HOVER_EXIT_PENDING
- const HOVER_EXIT_PENDING = MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
+ const HOVER_EXIT_PENDING = MotionEventFlag::HOVER_EXIT_PENDING.0 as u32;
/// FLAG_IS_GENERATED_GESTURE
- const IS_GENERATED_GESTURE = MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
+ const IS_GENERATED_GESTURE = MotionEventFlag::IS_GENERATED_GESTURE.0 as u32;
/// FLAG_CANCELED
- const CANCELED = INPUT_EVENT_FLAG_CANCELED as u32;
+ const CANCELED = MotionEventFlag::CANCELED.0 as u32;
/// FLAG_NO_FOCUS_CHANGE
- const NO_FOCUS_CHANGE = MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+ const NO_FOCUS_CHANGE = MotionEventFlag::NO_FOCUS_CHANGE.0 as u32;
+ /// PRIVATE_FLAG_SUPPORTS_ORIENTATION
+ const PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION.0 as u32;
+ /// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION
+ const PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION =
+ MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32;
/// FLAG_IS_ACCESSIBILITY_EVENT
- const IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
+ const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32;
/// FLAG_TAINTED
- const TAINTED = INPUT_EVENT_FLAG_TAINTED as u32;
+ const TAINTED = MotionEventFlag::TAINTED.0 as u32;
/// FLAG_TARGET_ACCESSIBILITY_FOCUS
- const TARGET_ACCESSIBILITY_FOCUS = MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
+ const TARGET_ACCESSIBILITY_FOCUS = MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS.0 as u32;
}
}
@@ -220,13 +233,128 @@
}
}
+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;
+ use crate::MotionFlags;
use crate::Source;
+ use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag;
+
#[test]
fn convert_source_class_pointer() {
let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap();
assert!(source.is_from_class(SourceClass::Pointer));
}
+
+ /// Ensure all MotionEventFlag constants are re-defined in rust.
+ #[test]
+ fn all_motion_event_flags_defined() {
+ for flag in MotionEventFlag::enum_values() {
+ assert!(
+ MotionFlags::from_bits(flag.0 as u32).is_some(),
+ "MotionEventFlag value {flag:?} is not redefined as a rust MotionFlags"
+ );
+ }
+ }
}
diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs
new file mode 100644
index 0000000..ab74efb
--- /dev/null
+++ b/libs/input/rust/keyboard_classification_config.rs
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+use crate::input::KeyboardType;
+
+// TODO(b/263559234): Categorize some of these to KeyboardType::None based on ability to produce
+// key events at all. (Requires setup allowing InputDevice to dynamically add/remove
+// KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends
+// up producing a key event)
+pub static CLASSIFIED_DEVICES: &[(
+ /* vendorId */ u16,
+ /* productId */ u16,
+ KeyboardType,
+ /* is_finalized */ bool,
+)] = &[
+ // HP X4000 Wireless Mouse
+ (0x03f0, 0xa407, KeyboardType::NonAlphabetic, true),
+ // Microsoft Wireless Mobile Mouse 6000
+ (0x045e, 0x0745, KeyboardType::NonAlphabetic, true),
+ // Microsoft Surface Precision Mouse
+ (0x045e, 0x0821, KeyboardType::NonAlphabetic, true),
+ // Microsoft Pro IntelliMouse
+ (0x045e, 0x082a, KeyboardType::NonAlphabetic, true),
+ // Microsoft Bluetooth Mouse
+ (0x045e, 0x082f, KeyboardType::NonAlphabetic, true),
+ // Xbox One Elite Series 2 gamepad
+ (0x045e, 0x0b05, KeyboardType::NonAlphabetic, true),
+ // Logitech T400
+ (0x046d, 0x4026, KeyboardType::NonAlphabetic, true),
+ // Logitech M720 Triathlon (Unifying)
+ (0x046d, 0x405e, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 2S (Unifying)
+ (0x046d, 0x4069, KeyboardType::NonAlphabetic, true),
+ // Logitech M585 (Unifying)
+ (0x046d, 0x406b, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Unifying)
+ (0x046d, 0x4072, KeyboardType::NonAlphabetic, true),
+ // Logitech Pebble M350
+ (0x046d, 0x4080, KeyboardType::NonAlphabetic, true),
+ // Logitech T630 Ultrathin
+ (0x046d, 0xb00d, KeyboardType::NonAlphabetic, true),
+ // Logitech M558
+ (0x046d, 0xb011, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master (Bluetooth)
+ (0x046d, 0xb012, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Bluetooth)
+ (0x046d, 0xb013, KeyboardType::NonAlphabetic, true),
+ // Logitech M720 Triathlon (Bluetooth)
+ (0x046d, 0xb015, KeyboardType::NonAlphabetic, true),
+ // Logitech M535
+ (0x046d, 0xb016, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master / Anywhere 2 (Bluetooth)
+ (0x046d, 0xb017, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 2S (Bluetooth)
+ (0x046d, 0xb019, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2S (Bluetooth)
+ (0x046d, 0xb01a, KeyboardType::NonAlphabetic, true),
+ // Logitech M585/M590 (Bluetooth)
+ (0x046d, 0xb01b, KeyboardType::NonAlphabetic, true),
+ // Logitech G603 Lightspeed Gaming Mouse (Bluetooth)
+ (0x046d, 0xb01c, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master (Bluetooth)
+ (0x046d, 0xb01e, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Bluetooth)
+ (0x046d, 0xb01f, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 3 (Bluetooth)
+ (0x046d, 0xb023, KeyboardType::NonAlphabetic, true),
+ // Logitech G604 Lightspeed Gaming Mouse (Bluetooth)
+ (0x046d, 0xb024, KeyboardType::NonAlphabetic, true),
+ // Logitech Spotlight Presentation Remote (Bluetooth)
+ (0x046d, 0xb503, KeyboardType::NonAlphabetic, true),
+ // Logitech R500 (Bluetooth)
+ (0x046d, 0xb505, KeyboardType::NonAlphabetic, true),
+ // Logitech M500s
+ (0x046d, 0xc093, KeyboardType::NonAlphabetic, true),
+ // Logitech Spotlight Presentation Remote (USB dongle)
+ (0x046d, 0xc53e, KeyboardType::NonAlphabetic, true),
+ // Elecom Enelo IR LED Mouse 350
+ (0x056e, 0x0134, KeyboardType::NonAlphabetic, true),
+ // Elecom EPRIM Blue LED 5 button mouse 228
+ (0x056e, 0x0141, KeyboardType::NonAlphabetic, true),
+ // Elecom Blue LED Mouse 203
+ (0x056e, 0x0159, KeyboardType::NonAlphabetic, true),
+ // Zebra LS2208 barcode scanner
+ (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true),
+ // RDing FootSwitch1F1
+ (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Sensei RAW Frost Blue
+ (0x1038, 0x1369, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Rival 3 Wired
+ (0x1038, 0x1824, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Rival 3 Wireless (USB dongle)
+ (0x1038, 0x1830, KeyboardType::NonAlphabetic, true),
+ // Yubico.com Yubikey
+ (0x1050, 0x0010, KeyboardType::NonAlphabetic, true),
+ // Yubico.com Yubikey 4 OTP+U2F+CCID
+ (0x1050, 0x0407, KeyboardType::NonAlphabetic, true),
+ // Lenovo USB-C Wired Compact Mouse
+ (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true),
+ // Corsair Katar Pro Wireless (USB dongle)
+ (0x1b1c, 0x1b94, KeyboardType::NonAlphabetic, true),
+ // Corsair Katar Pro Wireless (Bluetooth)
+ (0x1bae, 0x1b1c, KeyboardType::NonAlphabetic, true),
+ // Kensington Pro Fit Full-size
+ (0x1bcf, 0x08a0, KeyboardType::NonAlphabetic, true),
+ // Huion HS64
+ (0x256c, 0x006d, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Star G640
+ (0x28bd, 0x0914, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Artist 12 Pro
+ (0x28bd, 0x091f, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Deco mini7W
+ (0x28bd, 0x0928, KeyboardType::NonAlphabetic, true),
+];
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
new file mode 100644
index 0000000..8721ef7
--- /dev/null
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -0,0 +1,383 @@
+/*
+ * 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::keyboard_classification_config::CLASSIFIED_DEVICES;
+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)
+ };
+ }
+
+ // Check in known device list for classification
+ for data in CLASSIFIED_DEVICES.iter() {
+ if device.identifier.vendor == data.0 && device.identifier.product == data.1 {
+ return (data.2, data.3);
+ }
+ }
+
+ // 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_classification_config::CLASSIFIED_DEVICES;
+ 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));
+ }
+
+ #[test]
+ fn classify_known_devices() {
+ let mut classifier = KeyboardClassifier::new();
+ for device in CLASSIFIED_DEVICES.iter() {
+ classifier
+ .notify_keyboard_changed(create_device_with_vendor_product_ids(device.0, device.1));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), device.2);
+ assert_eq!(classifier.is_finalized(DEVICE_ID), device.3);
+ }
+ }
+
+ 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,
+ }
+ }
+
+ fn create_device_with_vendor_product_ids(vendor: u16, product: u16) -> 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,
+ product,
+ version: 567,
+ descriptor: "descriptor".to_string(),
+ },
+ classes: DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
+ }
+ }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index fb3f520..af8f889 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -18,9 +18,14 @@
mod input;
mod input_verifier;
+mod keyboard_classification_config;
+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 +52,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 +65,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 +147,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/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index bd50107..a1f917d 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -29,7 +29,6 @@
#include <GrDirectContext.h>
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
-#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
#include <android-base/stringprintf.h>
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 0a2f9b2..d2bb3d5 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -17,8 +17,6 @@
#ifndef SF_SKIAVKRENDERENGINE_H_
#define SF_SKIAVKRENDERENGINE_H_
-#include <vk/GrVkBackendContext.h>
-
#include "SkiaRenderEngine.h"
#include "VulkanInterface.h"
#include "compat/SkiaGpuContext.h"
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
index fc16c56..37b69f6 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -32,21 +32,8 @@
namespace renderengine {
namespace skia {
-GrVkBackendContext VulkanInterface::getGaneshBackendContext() {
- GrVkBackendContext backendContext;
- backendContext.fInstance = mInstance;
- backendContext.fPhysicalDevice = mPhysicalDevice;
- backendContext.fDevice = mDevice;
- backendContext.fQueue = mQueue;
- backendContext.fGraphicsQueueIndex = mQueueIndex;
- backendContext.fMaxAPIVersion = mApiVersion;
- backendContext.fVkExtensions = &mGrExtensions;
- backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2;
- backendContext.fGetProc = mGrGetProc;
- backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo;
- backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived
- backendContext.fDeviceLostProc = onVkDeviceFault;
- return backendContext;
+VulkanBackendContext VulkanInterface::getGaneshBackendContext() {
+ return this->getGraphiteBackendContext();
};
VulkanBackendContext VulkanInterface::getGraphiteBackendContext() {
@@ -57,7 +44,7 @@
backendContext.fQueue = mQueue;
backendContext.fGraphicsQueueIndex = mQueueIndex;
backendContext.fMaxAPIVersion = mApiVersion;
- backendContext.fVkExtensions = &mGrExtensions;
+ backendContext.fVkExtensions = &mVulkanExtensions;
backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2;
backendContext.fGetProc = mGrGetProc;
backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo;
@@ -197,7 +184,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);
}
@@ -427,11 +416,11 @@
mDeviceExtensionNames.push_back(devExt.extensionName);
}
- mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(),
- enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(),
- enabledDeviceExtensionNames.data());
+ mVulkanExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data());
- if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ if (!mVulkanExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
BAIL("Vulkan driver doesn't support external semaphore fd");
}
@@ -456,7 +445,7 @@
tailPnext = &mProtectedMemoryFeatures->pNext;
}
- if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
+ if (mVulkanExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT;
mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
mDeviceFaultFeatures->pNext = nullptr;
@@ -482,7 +471,7 @@
queuePriority,
};
- if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
+ if (mVulkanExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
queueNextPtr = &queuePriorityCreateInfo;
}
@@ -604,7 +593,7 @@
mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice.
mQueueIndex = 0;
mApiVersion = 0;
- mGrExtensions = GrVkExtensions();
+ mVulkanExtensions = skgpu::VulkanExtensions();
mGrGetProc = nullptr;
mIsProtected = false;
mIsRealtimePriority = false;
diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h
index af0489a..d0fe4d1 100644
--- a/libs/renderengine/skia/VulkanInterface.h
+++ b/libs/renderengine/skia/VulkanInterface.h
@@ -16,17 +16,14 @@
#pragma once
-#include <include/gpu/vk/GrVkBackendContext.h>
-#include <include/gpu/vk/GrVkExtensions.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
+#include <include/gpu/vk/VulkanExtensions.h>
+#include <include/gpu/vk/VulkanTypes.h>
#include <vulkan/vulkan.h>
using namespace skgpu;
-namespace skgpu {
-struct VulkanBackendContext;
-} // namespace skgpu
-
namespace android {
namespace renderengine {
namespace skia {
@@ -47,7 +44,8 @@
bool takeOwnership();
void teardown();
- GrVkBackendContext getGaneshBackendContext();
+ // TODO(b/309785258) Combine these into one now that they are the same implementation.
+ VulkanBackendContext getGaneshBackendContext();
VulkanBackendContext getGraphiteBackendContext();
VkSemaphore createExportableSemaphore();
VkSemaphore importSemaphoreFromSyncFd(int syncFd);
@@ -85,12 +83,12 @@
VkQueue mQueue = VK_NULL_HANDLE;
int mQueueIndex = 0;
uint32_t mApiVersion = 0;
- GrVkExtensions mGrExtensions;
+ skgpu::VulkanExtensions mVulkanExtensions;
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/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index b2eae00..b121fe8 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -25,7 +25,7 @@
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <include/gpu/gl/GrGLInterface.h>
-#include <include/gpu/vk/GrVkBackendContext.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
#include "../AutoBackendTexture.h"
#include "GaneshBackendTexture.h"
@@ -56,10 +56,10 @@
}
std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh(
- const GrVkBackendContext& grVkBackendContext,
+ const skgpu::VulkanBackendContext& vkBackendContext,
GrContextOptions::PersistentCache& skSLCacheMonitor) {
return std::make_unique<GaneshGpuContext>(
- GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor)));
+ GrDirectContexts::MakeVulkan(vkBackendContext, ganeshOptions(skSLCacheMonitor)));
}
GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) {
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index 282dfe7..9fa6fb8 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -23,7 +23,6 @@
#include <include/gpu/GrDirectContext.h>
#include <include/gpu/gl/GrGLInterface.h>
#include <include/gpu/graphite/Context.h>
-#include <include/gpu/vk/GrVkBackendContext.h>
#include "include/gpu/vk/VulkanBackendContext.h"
#include "SkiaBackendTexture.h"
@@ -52,10 +51,10 @@
GrContextOptions::PersistentCache& skSLCacheMonitor);
/**
- * grVkBackendContext must remain valid until after SkiaGpuContext is destroyed.
+ * vkBackendContext must remain valid until after SkiaGpuContext is destroyed.
*/
static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh(
- const GrVkBackendContext& grVkBackendContext,
+ const skgpu::VulkanBackendContext& vkBackendContext,
GrContextOptions::PersistentCache& skSLCacheMonitor);
// TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite.
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/Android.bp b/libs/sensor/Android.bp
index 7fa47b4..659666d 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -63,6 +63,8 @@
"libhardware",
"libpermission",
"android.companion.virtual.virtualdevice_aidl-cpp",
+ "libaconfig_storage_read_api_cc",
+ "server_configurable_flags",
],
static_libs: [
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 4438d45..84852ea 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -15,31 +15,40 @@
*/
#define LOG_TAG "Sensors"
-
-#include <sensor/SensorEventQueue.h>
-
-#include <algorithm>
-#include <sys/socket.h>
-
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-
-#include <sensor/Sensor.h>
-#include <sensor/BitTube.h>
-#include <sensor/ISensorEventConnection.h>
+#define ATRACE_TAG ATRACE_TAG_SYSTEM_SERVER
#include <android/sensor.h>
+#include <com_android_hardware_libsensor_flags.h>
+#include <cutils/trace.h>
#include <hardware/sensors-base.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
+#include <sensor/SensorManager.h>
+#include <sys/socket.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <string>
using std::min;
+namespace libsensor_flags = com::android::hardware::libsensor::flags;
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
-SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
- : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0),
- mNumAcksToSend(0) {
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection,
+ SensorManager& sensorManager)
+ : mSensorEventConnection(connection),
+ mRecBuffer(nullptr),
+ mSensorManager(sensorManager),
+ mAvailable(0),
+ mConsumed(0),
+ mNumAcksToSend(0) {
mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
}
@@ -65,8 +74,8 @@
ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
if (mAvailable == 0) {
- ssize_t err = BitTube::recvObjects(mSensorChannel,
- mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
+ ssize_t err =
+ BitTube::recvObjects(mSensorChannel, mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
if (err < 0) {
return err;
}
@@ -75,6 +84,20 @@
}
size_t count = min(numEvents, mAvailable);
memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
+
+ if (CC_UNLIKELY(ATRACE_ENABLED()) &&
+ libsensor_flags::sensor_event_queue_report_sensor_usage_in_tracing()) {
+ for (size_t i = 0; i < count; i++) {
+ std::optional<std::string_view> sensorName =
+ mSensorManager.getSensorNameByHandle(events->sensor);
+ if (sensorName.has_value()) {
+ char buffer[UINT8_MAX];
+ std::snprintf(buffer, sizeof(buffer), "Sensor event from %s",
+ sensorName.value().data());
+ ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer);
+ }
+ }
+ }
mAvailable -= count;
mConsumed += count;
return static_cast<ssize_t>(count);
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 9411e20..3ca6f0f 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -38,6 +38,7 @@
#include <sensor/SensorEventQueue.h>
#include <com_android_hardware_libsensor_flags.h>
+namespace libsensor_flags = com::android::hardware::libsensor::flags;
// ----------------------------------------------------------------------------
namespace android {
@@ -78,6 +79,21 @@
return DEVICE_ID_DEFAULT;
}
+bool findSensorNameInList(int32_t handle, const Vector<Sensor>& sensorList,
+ std::string* outString) {
+ for (auto& sensor : sensorList) {
+ if (sensor.getHandle() == handle) {
+ std::ostringstream oss;
+ oss << sensor.getStringType() << ":" << sensor.getName();
+ if (outString) {
+ *outString = std::move(oss.str());
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
Mutex SensorManager::sLock;
@@ -355,6 +371,25 @@
return nullptr;
}
+std::optional<std::string_view> SensorManager::getSensorNameByHandle(int32_t handle) {
+ std::lock_guard<std::mutex> lock(mSensorHandleToNameMutex);
+ auto iterator = mSensorHandleToName.find(handle);
+ if (iterator != mSensorHandleToName.end()) {
+ return iterator->second;
+ }
+
+ std::string sensorName;
+ if (!findSensorNameInList(handle, mSensors, &sensorName) &&
+ !findSensorNameInList(handle, mDynamicSensors, &sensorName)) {
+ ALOGW("Cannot find sensor with handle %d", handle);
+ return std::nullopt;
+ }
+
+ mSensorHandleToName[handle] = std::move(sensorName);
+
+ return mSensorHandleToName[handle];
+}
+
sp<SensorEventQueue> SensorManager::createEventQueue(
String8 packageName, int mode, String16 attributionTag) {
sp<SensorEventQueue> queue;
@@ -368,7 +403,7 @@
ALOGE("createEventQueue: connection is NULL.");
return nullptr;
}
- queue = new SensorEventQueue(connection);
+ queue = new SensorEventQueue(connection, *this);
break;
}
return queue;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8c3fde0..0bcaadc 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -42,6 +42,7 @@
// ----------------------------------------------------------------------------
class ISensorEventConnection;
+class SensorManager;
class Sensor;
class Looper;
@@ -65,7 +66,8 @@
// Default sensor sample period
static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
- explicit SensorEventQueue(const sp<ISensorEventConnection>& connection);
+ explicit SensorEventQueue(const sp<ISensorEventConnection>& connection,
+ SensorManager& sensorManager);
virtual ~SensorEventQueue();
virtual void onFirstRef();
@@ -107,6 +109,7 @@
mutable Mutex mLock;
mutable sp<Looper> mLooper;
ASensorEvent* mRecBuffer;
+ SensorManager& mSensorManager;
size_t mAvailable;
size_t mConsumed;
uint32_t mNumAcksToSend;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 49f050a..8d7237d 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -17,22 +17,20 @@
#ifndef ANDROID_GUI_SENSOR_MANAGER_H
#define ANDROID_GUI_SENSOR_MANAGER_H
-#include <map>
-#include <unordered_map>
-
-#include <stdint.h>
-#include <sys/types.h>
-
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <sensor/SensorEventQueue.h>
+#include <stdint.h>
+#include <sys/types.h>
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
-#include <utils/String8.h>
-#include <sensor/SensorEventQueue.h>
+#include <map>
+#include <string>
+#include <unordered_map>
// ----------------------------------------------------------------------------
// Concrete types for the NDK
@@ -66,6 +64,7 @@
sp<SensorEventQueue> createEventQueue(
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
+ std::optional<std::string_view> getSensorNameByHandle(int32_t handle);
bool isReplayDataInjectionEnabled();
bool isHalBypassReplayDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
@@ -97,6 +96,9 @@
const String16 mOpPackageName;
const int mDeviceId;
std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+
+ std::mutex mSensorHandleToNameMutex;
+ std::unordered_map<int32_t, std::string> mSensorHandleToName;
int32_t mDirectConnectionHandle;
};
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/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index f14a5cf..c9ec036 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -91,8 +91,7 @@
}
void* so = nullptr;
- // TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized
- if API_LEVEL_AT_LEAST(__ANDROID_API_FUTURE__, 202404) {
+ if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) {
so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
RTLD_LOCAL | RTLD_NOW);
} else {
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 02453ef..00dd6ba 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -21,6 +21,7 @@
#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
#endif
+#include <input/Keyboard.h>
#include <input/PrintTools.h>
#include <unordered_set>
@@ -30,10 +31,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 +103,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 +137,10 @@
mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
- mStylusPointerIconEnabled(false) {}
+ mStylusPointerIconEnabled(false),
+ mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
+ mRegisterListener(registerListener),
+ mUnregisterListener(unregisterListener) {}
PointerChoreographer::~PointerChoreographer() {
std::scoped_lock _l(mLock);
@@ -125,6 +148,7 @@
return;
}
mWindowInfoListener->onPointerChoreographerDestroyed();
+ mUnregisterListener(mWindowInfoListener);
}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
@@ -146,6 +170,7 @@
}
void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
+ fadeMouseCursorOnKeyPress(args);
mNextListener.notify(args);
}
@@ -155,6 +180,32 @@
mNextListener.notify(newArgs);
}
+void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
+ if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
+ return;
+ }
+ // Meta state for these keys is ignored for dismissing cursor while typing
+ constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
+ AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
+ if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
+ // Do not fade if any other meta state is active
+ return;
+ }
+ if (!mPolicy.isInputMethodConnectionActive()) {
+ return;
+ }
+
+ std::scoped_lock _l(mLock);
+ ui::LogicalDisplayId targetDisplay = args.displayId;
+ if (targetDisplay == ui::LogicalDisplayId::INVALID) {
+ targetDisplay = mCurrentFocusedDisplay;
+ }
+ auto it = mMousePointersByDisplay.find(targetDisplay);
+ if (it != mMousePointersByDisplay.end()) {
+ it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+}
+
NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
std::scoped_lock _l(mLock);
@@ -391,7 +442,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 +450,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
@@ -792,6 +835,11 @@
}
}
+void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
+ std::scoped_lock lock(mLock);
+ mCurrentFocusedDisplay = displayId;
+}
+
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 11c5a0c..aaf1e3e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -76,6 +76,11 @@
virtual void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) = 0;
/**
+ * Used by Dispatcher to notify changes in the current focused display.
+ */
+ virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
+
+ /**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
@@ -97,6 +102,7 @@
bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
ui::LogicalDisplayId displayId, DeviceId deviceId) override;
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
+ void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -108,10 +114,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:
@@ -128,6 +130,7 @@
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock);
+ void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
@@ -139,6 +142,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 +199,21 @@
bool mShowTouchesEnabled GUARDED_BY(mLock);
bool mStylusPointerIconEnabled GUARDED_BY(mLock);
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
+ ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock);
+
+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..a76271d 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);
}
}
@@ -877,7 +879,7 @@
class ScopedSyntheticEventTracer {
public:
ScopedSyntheticEventTracer(std::unique_ptr<trace::InputTracerInterface>& tracer)
- : mTracer(tracer) {
+ : mTracer(tracer), mProcessingTimestamp(now()) {
if (mTracer) {
mEventTracker = mTracer->createTrackerForSyntheticEvent();
}
@@ -885,7 +887,7 @@
~ScopedSyntheticEventTracer() {
if (mTracer) {
- mTracer->eventProcessingComplete(*mEventTracker);
+ mTracer->eventProcessingComplete(*mEventTracker, mProcessingTimestamp);
}
}
@@ -894,8 +896,9 @@
}
private:
- std::unique_ptr<trace::InputTracerInterface>& mTracer;
+ const std::unique_ptr<trace::InputTracerInterface>& mTracer;
std::unique_ptr<trace::EventTrackerInterface> mEventTracker;
+ const nsecs_t mProcessingTimestamp;
};
} // namespace
@@ -1261,7 +1264,7 @@
if (mTracer) {
if (auto& traceTracker = getTraceTracker(*mPendingEvent); traceTracker != nullptr) {
- mTracer->eventProcessingComplete(*traceTracker);
+ mTracer->eventProcessingComplete(*traceTracker, currentTime);
}
}
@@ -1894,8 +1897,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 +1913,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 +3318,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 +4884,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 +5132,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]);
}
}
@@ -5527,6 +5527,17 @@
synthesizeCancelationEventsForWindowLocked(windowHandle, options);
}
mFocusedDisplayId = displayId;
+ // Enqueue a command to run outside the lock to tell the policy that the focused display
+ // changed.
+ auto command = [this]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy.notifyFocusedDisplayChanged(mFocusedDisplayId);
+ };
+ postCommandLocked(std::move(command));
+
+ // 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);
@@ -6388,9 +6399,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 +6624,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 +6651,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 +6939,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/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 46e7e8b..dfbe02f 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -499,67 +499,53 @@
nsecs_t currentTime) {
std::vector<std::unique_ptr<MotionEntry>> events;
std::vector<uint32_t> canceledPointerIndices;
- std::vector<PointerProperties> pointerProperties(MAX_POINTERS);
- std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
+
for (uint32_t pointerIdx = 0; pointerIdx < memento.getPointerCount(); pointerIdx++) {
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
- pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
- pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
if (pointerIds.test(pointerId)) {
canceledPointerIndices.push_back(pointerIdx);
}
}
if (canceledPointerIndices.size() == memento.getPointerCount()) {
- const int32_t action =
- memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
- int32_t flags = memento.flags;
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- flags |= AMOTION_EVENT_FLAG_CANCELED;
+ // We are cancelling all pointers.
+ events.emplace_back(createCancelEntryForMemento(memento, currentTime));
+ return events;
+ }
+
+ // If we aren't canceling all pointers, we need to generate ACTION_POINTER_UP with
+ // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
+ // previously canceled pointers from PointerProperties and PointerCoords, and update
+ // pointerCount appropriately. For convenience, sort the canceled pointer indices in
+ // descending order so that we can just slide the remaining pointers to the beginning of
+ // the array when a pointer is canceled.
+ std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
+ std::greater<uint32_t>());
+
+ std::vector<PointerProperties> pointerProperties = memento.pointerProperties;
+ std::vector<PointerCoords> pointerCoords = memento.pointerCoords;
+ for (const uint32_t pointerIdx : canceledPointerIndices) {
+ if (pointerProperties.size() <= 1) {
+ LOG(FATAL) << "Unexpected code path for canceling all pointers!";
}
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
- /*actionButton=*/0, flags, AMETA_NONE,
- /*buttonState=*/0, MotionClassification::NONE,
+ /*actionButton=*/0,
+ memento.flags | AMOTION_EVENT_FLAG_CANCELED,
+ AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
- memento.pointerProperties, memento.pointerCoords));
- } else {
- // If we aren't canceling all pointers, we need to generate ACTION_POINTER_UP with
- // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
- // previously canceled pointers from PointerProperties and PointerCoords, and update
- // pointerCount appropriately. For convenience, sort the canceled pointer indices so that we
- // can just slide the remaining pointers to the beginning of the array when a pointer is
- // canceled.
- std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
- std::greater<uint32_t>());
+ pointerProperties, pointerCoords));
- uint32_t pointerCount = memento.getPointerCount();
- for (const uint32_t pointerIdx : canceledPointerIndices) {
- const int32_t action = pointerCount == 1 ? AMOTION_EVENT_ACTION_CANCEL
- : AMOTION_EVENT_ACTION_POINTER_UP |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
- currentTime, memento.deviceId, memento.source,
- memento.displayId, memento.policyFlags, action,
- /*actionButton=*/0,
- memento.flags | AMOTION_EVENT_FLAG_CANCELED,
- AMETA_NONE, /*buttonState=*/0,
- MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- pointerProperties, pointerCoords));
-
- // Cleanup pointer information
- pointerProperties.erase(pointerProperties.begin() + pointerIdx);
- pointerCoords.erase(pointerCoords.begin() + pointerIdx);
- pointerCount--;
- }
+ // Cleanup pointer information
+ pointerProperties.erase(pointerProperties.begin() + pointerIdx);
+ pointerCoords.erase(pointerCoords.begin() + pointerIdx);
}
return events;
}
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 0f03620..65fb76d 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -76,6 +76,11 @@
InputDeviceSensorAccuracy accuracy) = 0;
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
+ /*
+ * Notifies the system that focused display has changed.
+ */
+ virtual void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) = 0;
+
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
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/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index a1a87af..5d2b854 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -145,7 +145,8 @@
eventState->metadata.isSecure |= targetInfo.isSecureWindow;
}
-void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
+void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie,
+ nsecs_t processingTimestamp) {
if (isDerivedCookie(cookie)) {
LOG(FATAL) << "Event processing cannot be set from a derived cookie.";
}
@@ -154,7 +155,7 @@
LOG(FATAL) << "Traced event was already logged. "
"eventProcessingComplete() was likely called more than once.";
}
- eventState->onEventProcessingComplete();
+ eventState->onEventProcessingComplete(processingTimestamp);
}
std::unique_ptr<EventTrackerInterface> InputTracer::traceDerivedEvent(
@@ -242,7 +243,8 @@
// --- InputTracer::EventState ---
-void InputTracer::EventState::onEventProcessingComplete() {
+void InputTracer::EventState::onEventProcessingComplete(nsecs_t processingTimestamp) {
+ metadata.processingTimestamp = processingTimestamp;
metadata.isImeConnectionActive = tracer.mIsImeConnectionActive;
// Write all of the events known so far to the trace.
@@ -277,7 +279,7 @@
// We should never end up here in normal operation. However, in tests, it's possible that we
// stop and destroy InputDispatcher without waiting for it to finish processing events, at
// which point an event (and thus its EventState) may be destroyed before processing finishes.
- onEventProcessingComplete();
+ onEventProcessingComplete(systemTime(CLOCK_MONOTONIC));
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index ab175be..cb525a4 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -44,7 +44,8 @@
std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override;
std::unique_ptr<EventTrackerInterface> createTrackerForSyntheticEvent() override;
void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override;
- void eventProcessingComplete(const EventTrackerInterface&) override;
+ void eventProcessingComplete(const EventTrackerInterface&,
+ nsecs_t processingTimestamp) override;
std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&,
const EventTrackerInterface&) override;
void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) override;
@@ -61,7 +62,7 @@
explicit inline EventState(InputTracer& tracer) : tracer(tracer){};
~EventState();
- void onEventProcessingComplete();
+ void onEventProcessingComplete(nsecs_t processingTimestamp);
InputTracer& tracer;
std::vector<const TracedEvent> events;
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index af6eefb..f5e4e59 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -81,7 +81,8 @@
* outside of our control, such as how long apps take to respond, so we don't want to depend on
* that.
*/
- virtual void eventProcessingComplete(const EventTrackerInterface&) = 0;
+ virtual void eventProcessingComplete(const EventTrackerInterface&,
+ nsecs_t processingTimestamp) = 0;
/**
* Trace an input event that is derived from another event. This is used in cases where an event
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 25099c3..761d619 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -99,6 +99,8 @@
std::set<gui::Uid> targets;
// True if the there was an active input method connection while this event was processed.
bool isImeConnectionActive;
+ // The timestamp for when the dispatching decisions were made for the event by the system.
+ nsecs_t processingTimestamp;
};
/** Additional information about an input event being dispatched to a window. */
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 9b9633a..77b5c2e 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,11 @@
}
const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
- auto* inputEvent = tracePacket->set_android_input_event();
+ tracePacket->set_timestamp(metadata.processingTimestamp);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ 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 +259,11 @@
}
const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
- auto* inputEvent = tracePacket->set_android_input_event();
+ tracePacket->set_timestamp(metadata.processingTimestamp);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ 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 +287,11 @@
}
const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
- auto* inputEvent = tracePacket->set_android_input_event();
+ tracePacket->set_timestamp(dispatchArgs.deliveryTime);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ 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/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
index cae638f..5b94d57 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -21,6 +21,7 @@
#include <attestation/HmacKeyManager.h>
#include <input/Input.h>
#include <input/InputEventBuilders.h>
+#include <input/Keyboard.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
#include <vector>
@@ -206,6 +207,12 @@
return *this;
}
+ KeyArgsBuilder& metaState(int32_t metaState) {
+ mMetaState |= metaState;
+ mMetaState = normalizeMetaState(/*oldMetaState=*/mMetaState);
+ return *this;
+ }
+
NotifyKeyArgs build() const {
return {mEventId,
mEventTime,
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index f6dc109..7a85c12 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -55,6 +55,9 @@
*/
virtual void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
const FloatPoint& position) = 0;
+
+ /* Returns true if any InputConnection is currently active. */
+ virtual bool isInputMethodConnectionActive() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ba586d7..e76b648 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -91,7 +91,6 @@
"libutils",
],
static_libs: [
- "libc++fs",
"libchrome-gestures",
"libui-types",
],
@@ -156,7 +155,6 @@
},
},
static_libs: [
- "libc++fs",
"libchrome-gestures",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index fe70a51..65583e9 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -998,26 +998,23 @@
return *device->configuration;
}
-status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
- outAxisInfo->clear();
+std::optional<RawAbsoluteAxisInfo> EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis) const {
if (axis < 0 || axis > ABS_MAX) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
std::scoped_lock _l(mLock);
const Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
// We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid
// fd, because the info is populated once when the device is first opened, and it doesn't change
// throughout the device lifecycle.
auto it = device->absState.find(axis);
if (it == device->absState.end()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
- *outAxisInfo = it->second.info;
- return OK;
+ return it->second.info;
}
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -1130,22 +1127,20 @@
return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
-status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
- *outValue = 0;
+std::optional<int32_t> EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const {
if (axis < 0 || axis > ABS_MAX) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
std::scoped_lock _l(mLock);
const Device* device = getDeviceLocked(deviceId);
if (device == nullptr || !device->hasValidFd()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
const auto it = device->absState.find(axis);
if (it == device->absState.end()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
- *outValue = it->second.value;
- return OK;
+ return it->second.value;
}
base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
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..2a43466 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -21,6 +21,7 @@
#include <filesystem>
#include <functional>
#include <map>
+#include <optional>
#include <ostream>
#include <string>
#include <unordered_map>
@@ -85,64 +86,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 {
@@ -275,8 +279,8 @@
*/
virtual std::optional<PropertyMap> getConfiguration(int32_t deviceId) const = 0;
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const = 0;
+ virtual std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const = 0;
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
@@ -336,8 +340,7 @@
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const = 0;
+ virtual std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const = 0;
/* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size
* (slotCount + 1). The value at the 0 index is set to queried axis. */
virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
@@ -508,8 +511,8 @@
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override final;
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override final;
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override final;
bool hasRelativeAxis(int32_t deviceId, int axis) const override final;
@@ -556,8 +559,8 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final;
int32_t getKeyCodeForKeyLocation(int32_t deviceId,
int32_t locationKeyCode) const override final;
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override final;
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId,
+ int32_t axis) const override final;
base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
size_t slotCount) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4c9af2e..086c26f 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;
@@ -301,9 +306,12 @@
return mEventHub->getDeviceControllerNumber(mId);
}
inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
- if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) {
- return status;
+ std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
+ if (!info.has_value()) {
+ axisInfo->clear();
+ return NAME_NOT_FOUND;
}
+ *axisInfo = *info;
// Validate axis info for InputDevice.
if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
@@ -374,8 +382,8 @@
return mEventHub->getKeyCodeForKeyLocation(mId, locationKeyCode);
}
inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); }
- inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
- return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
+ inline std::optional<int32_t> getAbsoluteAxisValue(int32_t code) const {
+ return mEventHub->getAbsoluteAxisValue(mId, code);
}
inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis,
size_t slotCount) const {
@@ -427,9 +435,8 @@
}
inline bool hasAbsoluteAxis(int32_t code) const {
- RawAbsoluteAxisInfo info;
- mEventHub->getAbsoluteAxisInfo(mId, code, &info);
- return info.valid;
+ std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
+ return info.has_value() && info->valid;
}
inline bool isKeyPressed(int32_t scanCode) const {
return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
@@ -437,11 +444,6 @@
inline bool isKeyCodePressed(int32_t keyCode) const {
return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
}
- inline int32_t getAbsoluteAxisValue(int32_t code) const {
- int32_t value;
- mEventHub->getAbsoluteAxisValue(mId, code, &value);
- return value;
- }
inline bool isDeviceEnabled() { return mEventHub->isDeviceEnabled(mId); }
inline status_t enableDevice() { return mEventHub->enableDevice(mId); }
inline status_t disableDevice() { return mEventHub->disableDevice(mId); }
@@ -470,6 +472,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..25f4893 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;
@@ -483,7 +493,6 @@
void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
InputReaderContext& context = *getContext();
context.setLastKeyDownTimestamp(downTime);
- // TODO(b/338652288): Move cursor fading logic into PointerChoreographer.
// Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
// shortcuts
bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
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..81ec24e 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);
@@ -943,7 +931,7 @@
mSource |= AINPUT_SOURCE_STYLUS;
}
} else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
- mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+ mSource = AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DeviceMode::NAVIGATION;
} else {
ALOGW("Touch device '%s' has invalid parameters or configuration. The device will be "
@@ -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..8dc6e4d 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;
}
@@ -139,13 +139,11 @@
if (!mUsingSlotsProtocol) {
return;
}
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
+ if (const std::optional<int32_t> initialSlot = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT);
+ initialSlot.has_value()) {
+ mCurrentSlot = initialSlot.value();
} else {
- ALOGE("Could not retrieve current multi-touch slot index. status=%s",
- statusToString(status).c_str());
+ ALOGE("Could not retrieve current multi-touch slot index");
}
}
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..4cf9243 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -26,13 +26,13 @@
}
void SingleTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
- mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X);
- mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y);
- mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE);
- mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH);
- mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE);
- mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X);
- mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y);
+ mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X).value_or(0);
+ mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y).value_or(0);
+ mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE).value_or(0);
+ mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH).value_or(0);
+ mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE).value_or(0);
+ mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X).value_or(0);
+ mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y).value_or(0);
}
void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
@@ -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/Android.bp b/services/inputflinger/tests/Android.bp
index 9b5db23..cf0d46a 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -109,7 +109,6 @@
},
static_libs: [
"libflagtest",
- "libc++fs",
"libgmock",
],
require_root: true,
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index daa000f..12736c8 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -16,6 +16,8 @@
#include "FakeEventHub.h"
+#include <optional>
+
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <linux/input-event-codes.h>
@@ -263,18 +265,16 @@
return device->configuration;
}
-status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
+std::optional<RawAbsoluteAxisInfo> FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->absoluteAxes.indexOfKey(axis);
if (index >= 0) {
- *outAxisInfo = device->absoluteAxes.valueAt(index);
- return OK;
+ return device->absoluteAxes.valueAt(index);
}
}
- outAxisInfo->clear();
- return -1;
+ return std::nullopt;
}
bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -417,18 +417,15 @@
return AKEY_STATE_UNKNOWN;
}
-status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const {
+std::optional<int32_t> FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
if (index >= 0) {
- *outValue = device->absoluteAxisValue.valueAt(index);
- return OK;
+ return device->absoluteAxisValue.valueAt(index);
}
}
- *outValue = 0;
- return -1;
+ return std::nullopt;
}
void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis,
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index f07b344..c2c875f 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -168,8 +168,8 @@
InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
int32_t getDeviceControllerNumber(int32_t) const override;
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override;
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override;
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override;
bool hasRelativeAxis(int32_t deviceId, int axis) const override;
bool hasInputProperty(int32_t, int) const override;
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
@@ -187,7 +187,7 @@
std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override;
int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override;
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
// Return true if the device has non-empty key layout.
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index 530416c..3df05f4 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -215,6 +215,28 @@
mStaleEventTimeout = timeout;
}
+void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
+ mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+}
+
+void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (!mFocusedDisplayNotifiedCondition.wait_for(lock, 100ms,
+ [this, expectedDisplay]() REQUIRES(mLock) {
+ if (!mNotifiedFocusedDisplay.has_value() ||
+ mNotifiedFocusedDisplay.value() !=
+ expectedDisplay) {
+ return false;
+ }
+ return true;
+ })) {
+ ADD_FAILURE() << "Timed out waiting for notifyFocusedDisplayChanged(" << expectedDisplay
+ << ") to be called.";
+ }
+}
+
void FakeInputDispatcherPolicy::assertUserActivityNotPoked() {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -401,6 +423,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;
@@ -466,4 +491,10 @@
mFilteredEvent = nullptr;
}
+void FakeInputDispatcherPolicy::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) {
+ std::scoped_lock lock(mLock);
+ mNotifiedFocusedDisplay = displayId;
+ mFocusedDisplayNotifiedCondition.notify_all();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index 2c86146..a0f3ea9 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -115,6 +115,8 @@
void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
void assertUnhandledKeyReported(int32_t keycode);
void assertUnhandledKeyNotReported();
+ void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
+ void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay);
private:
std::mutex mLock;
@@ -125,6 +127,9 @@
std::condition_variable mPointerCaptureChangedCondition;
+ std::optional<ui::LogicalDisplayId> mNotifiedFocusedDisplay GUARDED_BY(mLock);
+ std::condition_variable mFocusedDisplayNotifiedCondition;
+
std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
@@ -144,6 +149,8 @@
std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
+ bool mConsumeKeyBeforeDispatching = false;
+
BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
std::condition_variable mNotifyUnhandledKey;
@@ -198,6 +205,7 @@
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override;
+ void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) override;
void assertFilterInputEventWasCalledInternal(
const std::function<void(const InputEvent&)>& verify);
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index d2cb0ac..6099c91 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -16,6 +16,7 @@
#include "FakeInputReaderPolicy.h"
+#include <android-base/properties.h>
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
@@ -24,6 +25,12 @@
namespace android {
+namespace {
+
+static const int HW_TIMEOUT_MULTIPLIER = base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+
+} // namespace
+
void FakeInputReaderPolicy::assertInputDevicesChanged() {
waitForInputDevices([](bool devicesChanged) {
if (!devicesChanged) {
@@ -241,9 +248,11 @@
base::ScopedLockAssertion assumeLocked(mLock);
const bool devicesChanged =
- mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
- return mInputDevicesChanged;
- });
+ mDevicesChangedCondition.wait_for(lock,
+ ADD_INPUT_DEVICE_TIMEOUT * HW_TIMEOUT_MULTIPLIER,
+ [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
mInputDevicesChanged = false;
}
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/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h
index 36a8f00..ee65d3a 100644
--- a/services/inputflinger/tests/FakeWindows.h
+++ b/services/inputflinger/tests/FakeWindows.h
@@ -304,15 +304,11 @@
WithFlags(expectedFlags)));
}
- inline void consumeMotionPointerUp(
- int32_t pointerIdx,
- ui::LogicalDisplayId expectedDisplayId = ui::LogicalDisplayId::DEFAULT,
- int32_t expectedFlags = 0) {
+ inline void consumeMotionPointerUp(int32_t pointerIdx,
+ const ::testing::Matcher<MotionEvent>& matcher) {
const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeMotionEvent(testing::AllOf(WithMotionAction(action),
- WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
+ consumeMotionEvent(testing::AllOf(WithMotionAction(action), matcher));
}
inline void consumeMotionUp(
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
index 8dfa8c8..643fab6 100644
--- a/services/inputflinger/tests/HardwareProperties_test.cpp
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -48,24 +48,20 @@
static constexpr int32_t EVENTHUB_ID = 1;
void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = true;
- outAxisInfo->minValue = min;
- outAxisInfo->maxValue = max;
- outAxisInfo->flat = 0;
- outAxisInfo->fuzz = 0;
- outAxisInfo->resolution = resolution;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(std::optional<RawAbsoluteAxisInfo>{{
+ .valid = true,
+ .minValue = min,
+ .maxValue = max,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = resolution,
+ }}));
}
void setupInvalidAxis(int axis) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = false;
- return -1;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(std::nullopt));
}
void setProperty(int property, bool value) {
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..5eab6be 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) {
@@ -1304,9 +1282,11 @@
injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- foregroundWindow->consumeMotionPointerUp(0);
- wallpaperWindow->consumeMotionPointerUp(0, ui::LogicalDisplayId::DEFAULT,
- EXPECTED_WALLPAPER_FLAGS);
+ foregroundWindow->consumeMotionPointerUp(/*pointerIdx=*/0,
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT));
+ wallpaperWindow->consumeMotionPointerUp(/*pointerIdx=*/0,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
@@ -6242,8 +6222,10 @@
{touchPoint, touchPoint}));
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionPointerUp(1, ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
+ WithPointerCount(2)));
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6385,8 +6367,10 @@
{pointInFirst, pointInSecond}));
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionPointerUp(1, ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
+ WithPointerCount(2)));
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6628,17 +6612,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 +6635,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 +6661,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 +6674,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 =
@@ -8584,6 +8583,8 @@
// Set focus to second display window.
// Set focus display to second one.
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
+
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
@@ -11054,6 +11055,38 @@
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);
+ mFakePolicy->assertFocusedDisplayNotified(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,
@@ -12854,10 +12887,14 @@
// Spy window pilfers the pointers.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
- window->consumeMotionPointerUp(/*idx=*/2, ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_CANCELED);
- window->consumeMotionPointerUp(/*idx=*/1, ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*pointerIdx=*/2,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED),
+ WithPointerCount(3)));
+ window->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED),
+ WithPointerCount(2)));
spy->assertNoEvents();
window->assertNoEvents();
@@ -13745,4 +13782,9 @@
/*pointerId=*/0));
}
+TEST_F(InputDispatcherTest, FocusedDisplayChangeIsNotified) {
+ mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index fea1349..19bc5be 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -57,16 +57,16 @@
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
int32_t resolution) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = valid;
- outAxisInfo->minValue = min;
- outAxisInfo->maxValue = max;
- outAxisInfo->flat = 0;
- outAxisInfo->fuzz = 0;
- outAxisInfo->resolution = resolution;
- return valid ? OK : -1;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
+ .valid = true,
+ .minValue = min,
+ .maxValue = max,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = resolution,
+ }}
+ : std::nullopt));
}
void InputMapperUnitTest::expectScanCodes(bool present, std::set<int> scanCodes) {
@@ -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..fa9d263 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) {
@@ -6255,7 +6245,7 @@
SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
- ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
@@ -6278,7 +6268,7 @@
InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/);
// Check whether device type update was successful.
- ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mDevice->getSources());
}
TEST_F(SingleTouchInputMapperTest, HoverEventsOutsidePhysicalFrameAreIgnored) {
@@ -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..db4e761 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,50 @@
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;
+ }
+
+ EXPECT_TRUE(packet.has_timestamp());
+ EXPECT_TRUE(packet.has_timestamp_clock_id());
+ EXPECT_EQ(packet.timestamp_clock_id(),
+ static_cast<uint32_t>(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC));
+
+ 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..bacc6d4 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 {
@@ -85,8 +91,8 @@
MOCK_METHOD(InputDeviceIdentifier, getDeviceIdentifier, (int32_t deviceId), (const));
MOCK_METHOD(int32_t, getDeviceControllerNumber, (int32_t deviceId), (const));
MOCK_METHOD(std::optional<PropertyMap>, getConfiguration, (int32_t deviceId), (const));
- MOCK_METHOD(status_t, getAbsoluteAxisInfo,
- (int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo), (const));
+ MOCK_METHOD(std::optional<RawAbsoluteAxisInfo>, getAbsoluteAxisInfo,
+ (int32_t deviceId, int axis), (const));
MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const));
MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const));
MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const));
@@ -125,7 +131,7 @@
MOCK_METHOD(int32_t, getKeyCodeState, (int32_t deviceId, int32_t keyCode), (const, override));
MOCK_METHOD(int32_t, getSwitchState, (int32_t deviceId, int32_t sw), (const, override));
- MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue),
+ MOCK_METHOD(std::optional<int32_t>, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis),
(const, override));
MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues,
(int32_t deviceId, int32_t axis, size_t slotCount), (const, override));
@@ -174,4 +180,13 @@
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));
+ MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
+};
+
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 031b77d..ab47cc6 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -67,17 +67,7 @@
EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
- AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- }
-
- void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) {
- for (int32_t keyCode : keyCodes) {
- process(EV_KEY, keyCode, 1);
- process(EV_SYN, SYN_REPORT, 0);
- process(EV_KEY, keyCode, 0);
- process(EV_SYN, SYN_REPORT, 0);
- }
+ AINPUT_SOURCE_KEYBOARD);
}
void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
@@ -96,42 +86,6 @@
};
/**
- * Pointer visibility should remain unaffected if there is no active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) {
- testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true);
-}
-
-/**
- * Pointer should hide if there is a active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
-}
-
-/**
- * Pointer should still hide if touchpad taps are already disabled
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithTouchpadTapDisabledHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).WillRepeatedly(Return(true));
- testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
-}
-
-/**
- * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is
- * active
- */
-TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
- KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
- KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
- testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true);
-}
-
-/**
* Touchpad tap should not be disabled if there is no active Input Method Connection
*/
TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index b5f8971..d4d3c38 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -99,11 +99,8 @@
setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
// reset current slot at the beginning
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
- .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) {
- *outValue = 0;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(0));
// mark all slots not in use
mockSlotValues({});
@@ -211,11 +208,8 @@
const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords;
// On buffer overflow mapper will be reset and MT slots data will be repopulated
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
- .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) {
- *outValue = 1;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(1));
mockSlotValues(
{{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}});
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..9a5b6a7 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,220 @@
assertPointerControllerRemoved(pc);
}
+class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest {
+protected:
+ const std::unordered_map<int32_t, int32_t>
+ mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON},
+ {AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON},
+ {AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON},
+ {AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON},
+ {AKEYCODE_SYM, AMETA_SYM_ON},
+ {AKEYCODE_FUNCTION, AMETA_FUNCTION_ON},
+ {AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON},
+ {AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON},
+ {AKEYCODE_META_LEFT, AMETA_META_LEFT_ON},
+ {AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON},
+ {AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON},
+ {AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON},
+ {AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON}};
+
+ void notifyKey(ui::LogicalDisplayId targetDisplay, int32_t keyCode,
+ int32_t metaState = AMETA_NONE) {
+ if (metaState == AMETA_NONE && mMetaKeyStates.contains(keyCode)) {
+ // For simplicity, we always set the corresponding meta state when sending a meta
+ // keycode. This does not take into consideration when the meta state is updated in
+ // reality.
+ metaState = mMetaKeyStates.at(keyCode);
+ }
+ mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .displayId(targetDisplay)
+ .keyCode(keyCode)
+ .metaState(metaState)
+ .build());
+ mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .displayId(targetDisplay)
+ .keyCode(keyCode)
+ .metaState(metaState)
+ .build());
+ }
+
+ void metaKeyCombinationHidesPointer(FakePointerController& pc, int32_t keyCode,
+ int32_t metaKeyCode) {
+ ASSERT_TRUE(pc.isPointerShown());
+ notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode));
+ ASSERT_FALSE(pc.isPointerShown());
+
+ unfadePointer();
+ }
+
+ void metaKeyCombinationDoesNotHidePointer(FakePointerController& pc, int32_t keyCode,
+ int32_t metaKeyCode) {
+ ASSERT_TRUE(pc.isPointerShown());
+ notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode));
+ ASSERT_TRUE(pc.isPointerShown());
+ }
+
+ void unfadePointer() {
+ // unfade pointer by injecting mose hover event
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ }
+};
+
+TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT);
+
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ notifyKey(DISPLAY_ID, AKEYCODE_0);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ unfadePointer();
+
+ notifyKey(DISPLAY_ID, AKEYCODE_A);
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT,
+ AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT,
+ AKEYCODE_SYM, AKEYCODE_FUNCTION,
+ AKEYCODE_CTRL_LEFT, AKEYCODE_CTRL_RIGHT,
+ AKEYCODE_META_LEFT, AKEYCODE_META_RIGHT,
+ AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+ AKEYCODE_SCROLL_LOCK};
+ for (int32_t keyCode : metaKeyCodes) {
+ notifyKey(ui::LogicalDisplayId::INVALID, keyCode);
+ }
+
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setFocusedDisplay(DISPLAY_ID);
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE);
+ auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
+ ASSERT_FALSE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+ unfadePointer();
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
+ ASSERT_FALSE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ // meta key combinations that should hide pointer
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_0, AKEYCODE_NUM_LOCK);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK);
+
+ // meta key combinations that should not hide pointer
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_RIGHT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_SYM);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_FUNCTION);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
+}
+
+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/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index ad48b0f..082bbb8 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -24,6 +24,9 @@
using std::chrono_literals::operator""ms;
+// Timeout for waiting for an input device to be added and processed
+static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 500ms;
+
// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 2b62dd1..1afb4f0 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -103,11 +103,8 @@
setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_))
- .WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) {
- *outValue = 0;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(0));
EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_))
.WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
return base::ResultError("Axis not supported", NAME_NOT_FOUND);
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..6dea540 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -17,6 +17,7 @@
#include <map>
#include <memory>
+#include <optional>
#include <EventHub.h>
#include <InputDevice.h>
@@ -119,16 +120,26 @@
void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) {
mAxes[deviceId][axis] = axisInfo;
}
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override {
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override {
if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) {
const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second;
if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) {
- *outAxisInfo = axisInfoIt->second;
- return OK;
+ return axisInfoIt->second;
}
}
- return mFdp->ConsumeIntegral<status_t>();
+ if (mFdp->ConsumeBool()) {
+ return std::optional<RawAbsoluteAxisInfo>({
+ .valid = mFdp->ConsumeBool(),
+ .minValue = mFdp->ConsumeIntegral<int32_t>(),
+ .maxValue = mFdp->ConsumeIntegral<int32_t>(),
+ .flat = mFdp->ConsumeIntegral<int32_t>(),
+ .fuzz = mFdp->ConsumeIntegral<int32_t>(),
+ .resolution = mFdp->ConsumeIntegral<int32_t>(),
+ });
+ } else {
+ return std::nullopt;
+ }
}
bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
bool hasInputProperty(int32_t deviceId, int property) const override {
@@ -197,9 +208,12 @@
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override {
- return mFdp->ConsumeIntegral<status_t>();
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override {
+ if (mFdp->ConsumeBool()) {
+ return mFdp->ConsumeIntegral<int32_t>();
+ } else {
+ return std::nullopt;
+ }
}
base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
size_t slotCount) const override {
@@ -338,9 +352,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/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index f6f104e..b2dc89b 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -26,6 +26,11 @@
"libfakeservicemanager",
"libcutils",
"liblog",
+ "libsensor_flags_c_lib",
+ ],
+ shared_libs: [
+ "libaconfig_storage_read_api_cc",
+ "server_configurable_flags",
],
srcs: [
"fuzzer.cpp",
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/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d420838..191d475 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -306,7 +306,7 @@
virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
- virtual void presentFrameAndReleaseLayers() = 0;
+ virtual void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>*) = 0;
@@ -314,6 +314,7 @@
const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentFrame() = 0;
+ virtual void executeCommands() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*> &outLayerRef) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d87968f..d1eff24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -60,6 +60,7 @@
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentFrame() override;
+ void executeCommands() override;
void setExpensiveRenderingExpected(bool) override;
void finishFrame(GpuCompositionResult&&) override;
bool supportsOffloadPresent() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index adcbbb9..9990a74 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -104,7 +104,7 @@
std::optional<base::unique_fd> composeSurfaces(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
- void presentFrameAndReleaseLayers() override;
+ void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
@@ -123,7 +123,8 @@
virtual std::future<bool> chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>*);
virtual void resetCompositionStrategy();
- virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync();
+ virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync(
+ bool flushEvenWhenDisabled);
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -137,6 +138,7 @@
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentFrame() override;
+ void executeCommands() override {}
virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings(
const std::shared_ptr<renderengine::ExternalTexture>& buffer) const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 3f3deae..d5bf2b5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -121,9 +121,10 @@
base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+ MOCK_METHOD(void, executeCommands, ());
MOCK_METHOD3(generateClientCompositionRequests,
std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 83b1b68..c1617d7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -361,6 +361,15 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
+void Display::executeCommands() {
+ const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayIdOpt) {
+ return;
+ }
+
+ getCompositionEngine().getHwComposer().executeCommands(*halDisplayIdOpt);
+}
+
compositionengine::Output::FrameFences Display::presentFrame() {
auto fences = impl::Output::presentFrame();
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 84f3f25..b40aea4 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -479,8 +479,9 @@
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
ftl::Future<std::monostate> future;
+ const bool flushEvenWhenDisabled = !refreshArgs.bufferIdsToUncache.empty();
if (mOffloadPresent) {
- future = presentFrameAndReleaseLayersAsync();
+ future = presentFrameAndReleaseLayersAsync(flushEvenWhenDisabled);
// Only offload for this frame. The next frame will determine whether it
// needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing,
@@ -488,7 +489,7 @@
// we don't want to churn.
mOffloadPresent = false;
} else {
- presentFrameAndReleaseLayers();
+ presentFrameAndReleaseLayers(flushEvenWhenDisabled);
future = ftl::yield<std::monostate>({});
}
renderCachedSets(refreshArgs);
@@ -1100,9 +1101,9 @@
finishPrepareFrame();
}
-ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() {
- return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() {
- presentFrameAndReleaseLayers();
+ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
+ return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
+ presentFrameAndReleaseLayers(flushEvenWhenDisabled);
return true;
})))
.then([](bool) { return std::monostate{}; });
@@ -1177,7 +1178,8 @@
}
}
- presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
@@ -1567,11 +1569,16 @@
return false;
}
-void Output::presentFrameAndReleaseLayers() {
+void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
+ if (flushEvenWhenDisabled && FlagManager::getInstance().flush_buffer_slots_to_uncache()) {
+ // Some commands, like clearing buffer slots, should still be executed
+ // even if the display is not enabled.
+ executeCommands();
+ }
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index a95a5c6..39163ea 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -1067,8 +1067,8 @@
EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
-
- mDisplay->presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mDisplay->presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 4612117..629d9f2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -63,6 +63,7 @@
(override));
MOCK_METHOD2(presentAndGetReleaseFences,
status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
+ MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 0dc3c9f..c34168d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2014,7 +2014,7 @@
MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine),
@@ -2046,7 +2046,7 @@
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2067,7 +2067,7 @@
EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2913,7 +2913,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -2950,7 +2950,8 @@
mOutput.mState.isEnabled = false;
InSequence seq;
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2962,7 +2963,8 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2978,7 +2980,8 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2996,7 +2999,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
@@ -3139,7 +3142,8 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+ MOCK_METHOD(compositionengine::Output::FrameFences, presentFrame, ());
+ MOCK_METHOD(void, executeCommands, ());
};
struct Layer {
@@ -3177,9 +3181,67 @@
};
TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
mOutput.mState.isEnabled = false;
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+ EXPECT_CALL(mOutput, presentFrame()).Times(0);
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledExecutesCommandsIfFlush) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = false;
+ EXPECT_CALL(mOutput, executeCommands());
+ EXPECT_CALL(mOutput, presentFrame()).Times(0);
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // This should only be called for disabled outputs. This test's goal is to verify this line;
+ // the other expectations help satisfy the StrictMocks.
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands2) {
+ // Same test as ifEnabledDoNotExecuteCommands, but with this variable set to false.
+ constexpr bool kFlushEvenWhenDisabled = false;
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // This should only be called for disabled outputs. This test's goal is to verify this line;
+ // the other expectations help satisfy the StrictMocks.
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3197,7 +3259,8 @@
EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
@@ -3241,7 +3304,8 @@
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
@@ -3282,7 +3346,8 @@
EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
@@ -3308,7 +3373,8 @@
EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
@@ -3333,7 +3399,8 @@
EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
@@ -3381,7 +3448,8 @@
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
// After the call the list of released layers should have been cleared.
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
@@ -3429,7 +3497,8 @@
EXPECT_EQ(FenceResult(presentFence), fenceResult);
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
// After the call the list of released layers should have been cleared.
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
@@ -5272,8 +5341,9 @@
struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock {
// Set up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
- MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
+ MOCK_METHOD(ftl::Future<std::monostate>, presentFrameAndReleaseLayersAsync,
+ (bool flushEvenWhenDisabled));
};
OutputPresentFrameAndReleaseLayersAsyncTest() {
mOutput->setDisplayColorProfileForTest(
@@ -5290,16 +5360,16 @@
};
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) {
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0);
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(_)).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(1);
mOutput->present(mRefreshArgs);
}
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) {
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(false))
.WillOnce(Return(ftl::yield<std::monostate>({})));
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(0);
mOutput->offloadPresentNextFrame();
mOutput->present(mRefreshArgs);
@@ -5307,9 +5377,10 @@
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) {
::testing::InSequence inseq;
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(kFlushEvenWhenDisabled))
.WillOnce(Return(ftl::yield<std::monostate>({})));
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled)).Times(1);
mOutput->offloadPresentNextFrame();
mOutput->present(mRefreshArgs);
@@ -5390,5 +5461,59 @@
mOutput.updateProtectedContentState();
}
+struct OutputPresentFrameAndReleaseLayersTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test (and functions we can
+ // ignore) to use mock implementations.
+ MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(updateCompositionState,
+ void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(beginFrame, void());
+ MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
+ MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
+ MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+ MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine),
+ (override));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, (), (override));
+ };
+
+ OutputPresentFrameAndReleaseLayersTest() {
+ EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
+ }
+
+ NiceMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, noBuffersToUncache) {
+ CompositionRefreshArgs args;
+ ASSERT_TRUE(args.bufferIdsToUncache.empty());
+ mOutput.editState().isEnabled = false;
+
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+ mOutput.present(args);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, buffersToUncache) {
+ CompositionRefreshArgs args;
+ args.bufferIdsToUncache.push_back(1);
+ mOutput.editState().isEnabled = false;
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+ mOutput.present(args);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index f093384..a6a9bec 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -20,30 +20,221 @@
#include "Display/DisplayModeController.h"
#include "Display/DisplaySnapshot.h"
+#include "DisplayHardware/HWComposer.h"
+#include <common/FlagManager.h>
+#include <ftl/concat.h>
+#include <ftl/expected.h>
#include <log/log.h>
namespace android::display {
+template <size_t N>
+inline std::string DisplayModeController::Display::concatId(const char (&str)[N]) const {
+ return std::string(ftl::Concat(str, ' ', snapshot.get().displayId().value).str());
+}
+
+DisplayModeController::Display::Display(DisplaySnapshotRef snapshot,
+ RefreshRateSelectorPtr selectorPtr)
+ : snapshot(snapshot),
+ selectorPtr(std::move(selectorPtr)),
+ pendingModeFpsTrace(concatId("PendingModeFps")),
+ activeModeFpsTrace(concatId("ActiveModeFps")),
+ renderRateFpsTrace(concatId("RenderRateFps")),
+ hasDesiredModeTrace(concatId("HasDesiredMode"), false) {}
+
+void DisplayModeController::registerDisplay(PhysicalDisplayId displayId,
+ DisplaySnapshotRef snapshotRef,
+ RefreshRateSelectorPtr selectorPtr) {
+ std::lock_guard lock(mDisplayLock);
+ mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr));
+}
+
void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
DisplayModeId activeModeId,
scheduler::RefreshRateSelector::Config config) {
const auto& snapshot = snapshotRef.get();
const auto displayId = snapshot.displayId();
- mDisplays.emplace_or_replace(displayId, snapshotRef, snapshot.displayModes(), activeModeId,
- config);
+ std::lock_guard lock(mDisplayLock);
+ mDisplays.emplace_or_replace(displayId,
+ std::make_unique<Display>(snapshotRef, snapshot.displayModes(),
+ activeModeId, config));
}
void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
const bool ok = mDisplays.erase(displayId);
ALOGE_IF(!ok, "%s: Unknown display %s", __func__, to_string(displayId).c_str());
}
-auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) -> RefreshRateSelectorPtr {
+auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) const
+ -> RefreshRateSelectorPtr {
+ std::lock_guard lock(mDisplayLock);
return mDisplays.get(displayId)
- .transform([](const Display& display) { return display.selectorPtr; })
+ .transform([](const DisplayPtr& displayPtr) { return displayPtr->selectorPtr; })
.value_or(nullptr);
}
+auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId,
+ DisplayModeRequest&& desiredMode) -> DesiredModeAction {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get();
+
+ {
+ ATRACE_NAME(displayPtr->concatId(__func__).c_str());
+ ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str());
+
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+
+ if (auto& desiredModeOpt = displayPtr->desiredModeOpt) {
+ // A mode transition was already scheduled, so just override the desired mode.
+ const bool emitEvent = desiredModeOpt->emitEvent;
+ const bool force = desiredModeOpt->force;
+ desiredModeOpt = std::move(desiredMode);
+ desiredModeOpt->emitEvent |= emitEvent;
+ if (FlagManager::getInstance().connected_display()) {
+ desiredModeOpt->force |= force;
+ }
+ return DesiredModeAction::None;
+ }
+
+ // If the desired mode is already active...
+ const auto activeMode = displayPtr->selectorPtr->getActiveMode();
+ if (const auto& desiredModePtr = desiredMode.mode.modePtr;
+ !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
+ if (activeMode == desiredMode.mode) {
+ return DesiredModeAction::None;
+ }
+
+ // ...but the render rate changed:
+ setActiveModeLocked(displayId, desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
+ desiredMode.mode.fps);
+ return DesiredModeAction::InitiateRenderRateSwitch;
+ }
+
+ // Restore peak render rate to schedule the next frame as soon as possible.
+ setActiveModeLocked(displayId, activeMode.modePtr->getId(),
+ activeMode.modePtr->getVsyncRate(), activeMode.modePtr->getPeakFps());
+
+ // Initiate a mode change.
+ displayPtr->desiredModeOpt = std::move(desiredMode);
+ displayPtr->hasDesiredModeTrace = true;
+ }
+
+ return DesiredModeAction::InitiateDisplayModeSwitch;
+}
+
+auto DisplayModeController::getDesiredMode(PhysicalDisplayId displayId) const
+ -> DisplayModeRequestOpt {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->desiredModeOpt;
+ }
+}
+
+auto DisplayModeController::getPendingMode(PhysicalDisplayId displayId) const
+ -> DisplayModeRequestOpt {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->pendingModeOpt;
+ }
+}
+
+bool DisplayModeController::isModeSetPending(PhysicalDisplayId displayId) const {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->isModeSetPending;
+ }
+}
+
+scheduler::FrameRateMode DisplayModeController::getActiveMode(PhysicalDisplayId displayId) const {
+ return selectorPtrFor(displayId)->getActiveMode();
+}
+
+void DisplayModeController::clearDesiredMode(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ displayPtr->desiredModeOpt.reset();
+ displayPtr->hasDesiredModeTrace = false;
+ }
+}
+
+bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
+ DisplayModeRequest&& desiredMode,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline& outTimeline) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+ // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
+ // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
+ // cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
+ // consumed at this point, so clear the `force` flag to prevent an endless loop of
+ // `initiateModeChange`.
+ if (FlagManager::getInstance().connected_display()) {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ if (displayPtr->desiredModeOpt) {
+ displayPtr->desiredModeOpt->force = false;
+ }
+ }
+
+ displayPtr->pendingModeOpt = std::move(desiredMode);
+ displayPtr->isModeSetPending = true;
+
+ const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
+
+ if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
+ &outTimeline) != OK) {
+ return false;
+ }
+
+ ATRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ return true;
+}
+
+void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ std::lock_guard lock(mDisplayLock);
+ setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ displayPtr->isModeSetPending = false;
+}
+
+void DisplayModeController::setActiveMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ std::lock_guard lock(mDisplayLock);
+ setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+}
+
+void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ ATRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue());
+ ATRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue());
+
+ displayPtr->selectorPtr->setActiveMode(modeId, renderFps);
+
+ if (mActiveModeListener) {
+ mActiveModeListener(displayId, vsyncRate, renderFps);
+ }
+}
+
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index b6a6bee..258b04b 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -17,16 +17,26 @@
#pragma once
#include <memory>
+#include <mutex>
+#include <string>
#include <utility>
#include <android-base/thread_annotations.h>
+#include <ftl/function.h>
+#include <ftl/optional.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>
+#include "Display/DisplayModeRequest.h"
#include "Display/DisplaySnapshotRef.h"
#include "DisplayHardware/DisplayMode.h"
#include "Scheduler/RefreshRateSelector.h"
#include "ThreadContext.h"
+#include "TracedOrdinal.h"
+
+namespace android {
+class HWComposer;
+} // namespace android
namespace android::display {
@@ -34,40 +44,97 @@
// certain heuristic signals.
class DisplayModeController {
public:
- // The referenced DisplaySnapshot must outlive the registration.
- void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
- REQUIRES(kMainThreadContext);
- void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+ using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>;
- // TODO(b/241285876): Remove once ownership is no longer shared with DisplayDevice.
+ DisplayModeController() = default;
+
+ void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; }
+ void setActiveModeListener(const ActiveModeListener& listener) {
+ mActiveModeListener = listener;
+ }
+
+ // TODO: b/241285876 - Remove once ownership is no longer shared with DisplayDevice.
using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
- // Returns `nullptr` if the display is no longer registered (or never was).
- RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) REQUIRES(kMainThreadContext);
-
// Used by tests to inject an existing RefreshRateSelector.
- // TODO(b/241285876): Remove this.
- void registerDisplay(PhysicalDisplayId displayId, DisplaySnapshotRef snapshotRef,
- RefreshRateSelectorPtr selectorPtr) {
- mDisplays.emplace_or_replace(displayId, snapshotRef, selectorPtr);
- }
+ // TODO: b/241285876 - Remove this.
+ void registerDisplay(PhysicalDisplayId, DisplaySnapshotRef, RefreshRateSelectorPtr)
+ EXCLUDES(mDisplayLock);
+
+ // The referenced DisplaySnapshot must outlive the registration.
+ void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ // Returns `nullptr` if the display is no longer registered (or never was).
+ RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+ enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+
+ DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
+ EXCLUDES(mDisplayLock);
+
+ using DisplayModeRequestOpt = ftl::Optional<DisplayModeRequest>;
+
+ DisplayModeRequestOpt getDesiredMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+ void clearDesiredMode(PhysicalDisplayId) EXCLUDES(mDisplayLock);
+
+ DisplayModeRequestOpt getPendingMode(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+ bool isModeSetPending(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+ bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline& outTimeline)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ EXCLUDES(mDisplayLock);
private:
struct Display {
- Display(DisplaySnapshotRef snapshot, RefreshRateSelectorPtr selectorPtr)
- : snapshot(snapshot), selectorPtr(std::move(selectorPtr)) {}
+ template <size_t N>
+ std::string concatId(const char (&)[N]) const;
+ Display(DisplaySnapshotRef, RefreshRateSelectorPtr);
Display(DisplaySnapshotRef snapshot, DisplayModes modes, DisplayModeId activeModeId,
scheduler::RefreshRateSelector::Config config)
: Display(snapshot,
std::make_shared<scheduler::RefreshRateSelector>(std::move(modes),
activeModeId, config)) {}
-
const DisplaySnapshotRef snapshot;
const RefreshRateSelectorPtr selectorPtr;
+
+ const std::string pendingModeFpsTrace;
+ const std::string activeModeFpsTrace;
+ const std::string renderRateFpsTrace;
+
+ std::mutex desiredModeLock;
+ DisplayModeRequestOpt desiredModeOpt GUARDED_BY(desiredModeLock);
+ TracedOrdinal<bool> hasDesiredModeTrace GUARDED_BY(desiredModeLock);
+
+ DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
+ bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
- ui::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays;
+ using DisplayPtr = std::unique_ptr<Display>;
+
+ void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ REQUIRES(mDisplayLock);
+
+ // Set once when initializing the DisplayModeController, which the HWComposer must outlive.
+ HWComposer* mComposerPtr = nullptr;
+
+ ActiveModeListener mActiveModeListener;
+
+ mutable std::mutex mDisplayLock;
+ ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock);
};
} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 38cf053..27ea4a9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -24,7 +24,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -36,6 +35,7 @@
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <configstore/Utils.h>
+#include <ftl/concat.h>
#include <log/log.h>
#include <system/window.h>
@@ -64,15 +64,11 @@
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mCompositionDisplay{args.compositionDisplay},
- mPendingModeFpsTrace(concatId("PendingModeFps")),
- mActiveModeFpsTrace(concatId("ActiveModeFps")),
- mRenderRateFpsTrace(concatId("RenderRateFps")),
mPhysicalOrientation(args.physicalOrientation),
mPowerMode(ftl::Concat("PowerMode ", getId().value).c_str(), args.initialPowerMode),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
- mRefreshRateSelector(std::move(args.refreshRateSelector)),
- mHasDesiredModeTrace(concatId("HasDesiredMode"), false) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->editState().isProtected = args.isProtected;
mCompositionDisplay->createRenderSurface(
@@ -204,47 +200,6 @@
return mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- ATRACE_INT(mActiveModeFpsTrace.c_str(), vsyncRate.getIntValue());
- ATRACE_INT(mRenderRateFpsTrace.c_str(), renderFps.getIntValue());
-
- mRefreshRateSelector->setActiveMode(modeId, renderFps);
- updateRefreshRateOverlayRate(vsyncRate, renderFps);
-}
-
-bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline& outTimeline) {
- // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For
- // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared
- // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed
- // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`.
- if (FlagManager::getInstance().connected_display()) {
- std::scoped_lock lock(mDesiredModeLock);
- if (mDesiredModeOpt) {
- mDesiredModeOpt->force = false;
- }
- }
-
- mPendingModeOpt = std::move(desiredMode);
- mIsModeSetPending = true;
-
- const auto& mode = *mPendingModeOpt->mode.modePtr;
-
- if (mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode.getHwcId(), constraints,
- &outTimeline) != OK) {
- return false;
- }
-
- ATRACE_INT(mPendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
- return true;
-}
-
-void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- setActiveMode(modeId, vsyncRate, renderFps);
- mIsModeSetPending = false;
-}
-
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
const auto physicalId = getPhysicalId();
if (!mHwComposer.isConnected(physicalId)) {
@@ -450,8 +405,9 @@
}
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
- bool showRenderRate, bool showInMiddle) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, Fps refreshRate,
+ Fps renderFps, bool showSpinner, bool showRenderRate,
+ bool showInMiddle) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
@@ -474,14 +430,12 @@
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) {
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
- setByHwc);
+ updateRefreshRateOverlayRate(refreshRate, renderFps, setByHwc);
}
}
@@ -489,6 +443,9 @@
ATRACE_CALL();
if (mRefreshRateOverlay) {
if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) {
+ if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) {
+ refreshRate = renderFps;
+ }
mRefreshRateOverlay->changeRefreshRate(refreshRate, renderFps);
} else {
mRefreshRateOverlay->changeRenderRate(renderFps);
@@ -529,57 +486,6 @@
}
}
-auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction {
- ATRACE_NAME(concatId(__func__).c_str());
- ALOGD("%s %s", concatId(__func__).c_str(), to_string(desiredMode).c_str());
-
- std::scoped_lock lock(mDesiredModeLock);
- if (mDesiredModeOpt) {
- // A mode transition was already scheduled, so just override the desired mode.
- const bool emitEvent = mDesiredModeOpt->emitEvent;
- const bool force = mDesiredModeOpt->force;
- mDesiredModeOpt = std::move(desiredMode);
- mDesiredModeOpt->emitEvent |= emitEvent;
- if (FlagManager::getInstance().connected_display()) {
- mDesiredModeOpt->force |= force;
- }
- return DesiredModeAction::None;
- }
-
- // If the desired mode is already active...
- const auto activeMode = refreshRateSelector().getActiveMode();
- if (const auto& desiredModePtr = desiredMode.mode.modePtr;
- !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
- if (activeMode == desiredMode.mode) {
- return DesiredModeAction::None;
- }
-
- // ...but the render rate changed:
- setActiveMode(desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
- desiredMode.mode.fps);
- return DesiredModeAction::InitiateRenderRateSwitch;
- }
-
- setActiveMode(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
- activeMode.modePtr->getPeakFps());
-
- // Initiate a mode change.
- mDesiredModeOpt = std::move(desiredMode);
- mHasDesiredModeTrace = true;
- return DesiredModeAction::InitiateDisplayModeSwitch;
-}
-
-auto DisplayDevice::getDesiredMode() const -> DisplayModeRequestOpt {
- std::scoped_lock lock(mDesiredModeLock);
- return mDesiredModeOpt;
-}
-
-void DisplayDevice::clearDesiredMode() {
- std::scoped_lock lock(mDesiredModeLock);
- mDesiredModeOpt.reset();
- mHasDesiredModeTrace = false;
-}
-
void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
using fps_approx_ops::operator<=;
if (mRequestedRefreshRate <= 0_Hz) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a21559f..3cc8cf5 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -23,8 +23,6 @@
#include <android-base/thread_annotations.h>
#include <android/native_window.h>
#include <binder/IBinder.h>
-#include <ftl/concat.h>
-#include <ftl/optional.h>
#include <gui/LayerState.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
@@ -42,7 +40,6 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include "Display/DisplayModeRequest.h"
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
@@ -183,37 +180,6 @@
ui::Dataspace getCompositionDataSpace() const;
- /* ------------------------------------------------------------------------
- * Display mode management.
- */
-
- enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
-
- DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock);
-
- using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
-
- DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock);
- void clearDesiredMode() EXCLUDES(mDesiredModeLock);
-
- DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) {
- return mPendingModeOpt;
- }
- bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
-
- scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
- return mRefreshRateSelector->getActiveMode();
- }
-
- void setActiveMode(DisplayModeId, Fps vsyncRate, Fps renderFps);
-
- bool initiateModeChange(display::DisplayModeRequest&&, const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline& outTimeline)
- REQUIRES(kMainThreadContext);
-
- void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
- REQUIRES(kMainThreadContext);
-
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
// Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
@@ -221,13 +187,14 @@
return mRefreshRateSelector;
}
- void animateOverlay();
-
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
- bool showInMiddle) REQUIRES(kMainThreadContext);
+ // TODO(b/241285876): Move overlay to DisplayModeController.
+ void enableRefreshRateOverlay(bool enable, bool setByHwc, Fps refreshRate, Fps renderFps,
+ bool showSpinner, bool showRenderRate, bool showInMiddle)
+ REQUIRES(kMainThreadContext);
void updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, bool setByHwc = false);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
+ void animateOverlay();
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
// Enables an overlay to be display with the hdr/sdr ratio
@@ -249,11 +216,6 @@
void dump(utils::Dumper&) const;
private:
- template <size_t N>
- inline std::string concatId(const char (&str)[N]) const {
- return std::string(ftl::Concat(str, ' ', getId().value).str());
- }
-
const sp<SurfaceFlinger> mFlinger;
HWComposer& mHwComposer;
const wp<IBinder> mDisplayToken;
@@ -262,9 +224,6 @@
const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
std::string mDisplayName;
- std::string mPendingModeFpsTrace;
- std::string mActiveModeFpsTrace;
- std::string mRenderRateFpsTrace;
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
@@ -296,13 +255,6 @@
std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay;
// This parameter is only used for hdr/sdr ratio overlay
float mHdrSdrRatio = 1.0f;
-
- mutable std::mutex mDesiredModeLock;
- DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock);
- TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock);
-
- DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext);
- bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 84f668d..8c0f81e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -79,6 +79,9 @@
DisplayType type)
: mComposer(composer), mCapabilities(capabilities), mId(id), mType(type) {
ALOGV("Created display %" PRIu64, id);
+ if (mType == hal::DisplayType::VIRTUAL) {
+ loadDisplayCapabilities();
+ }
}
Display::~Display() {
@@ -499,29 +502,7 @@
auto intError = mComposer.setPowerMode(mId, intMode);
if (mode == PowerMode::ON) {
- std::call_once(mDisplayCapabilityQueryFlag, [this]() {
- std::vector<DisplayCapability> tmpCapabilities;
- auto error =
- static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
- if (error == Error::NONE) {
- std::scoped_lock lock(mDisplayCapabilitiesMutex);
- mDisplayCapabilities.emplace();
- for (auto capability : tmpCapabilities) {
- mDisplayCapabilities->emplace(capability);
- }
- } else if (error == Error::UNSUPPORTED) {
- std::scoped_lock lock(mDisplayCapabilitiesMutex);
- mDisplayCapabilities.emplace();
- if (mCapabilities.count(AidlCapability::SKIP_CLIENT_COLOR_TRANSFORM)) {
- mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
- }
- bool dozeSupport = false;
- error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
- if (error == Error::NONE && dozeSupport) {
- mDisplayCapabilities->emplace(DisplayCapability::DOZE);
- }
- }
- });
+ loadDisplayCapabilities();
}
return static_cast<Error>(intError);
@@ -653,6 +634,32 @@
auto it = mLayers.find(id);
return it != mLayers.end() ? it->second.lock() : nullptr;
}
+
+void Display::loadDisplayCapabilities() {
+ std::call_once(mDisplayCapabilityQueryFlag, [this]() {
+ std::vector<DisplayCapability> tmpCapabilities;
+ auto error =
+ static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
+ if (error == Error::NONE) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
+ for (auto capability : tmpCapabilities) {
+ mDisplayCapabilities->emplace(capability);
+ }
+ } else if (error == Error::UNSUPPORTED) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
+ if (mCapabilities.count(AidlCapability::SKIP_CLIENT_COLOR_TRANSFORM)) {
+ mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+ }
+ bool dozeSupport = false;
+ error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
+ if (error == Error::NONE && dozeSupport) {
+ mDisplayCapabilities->emplace(DisplayCapability::DOZE);
+ }
+ }
+ });
+}
} // namespace impl
// Layer methods
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index de044e0..5b94831 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -278,6 +278,7 @@
hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override;
private:
+ void loadDisplayCapabilities();
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3cfb9ca..3d285a8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -600,6 +600,13 @@
return NO_ERROR;
}
+status_t HWComposer::executeCommands(HalDisplayId displayId) {
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId()));
+ RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index bc32cda..9368b7b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,6 +160,8 @@
HalDisplayId,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) = 0;
+ virtual status_t executeCommands(HalDisplayId) = 0;
+
// set power mode
virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -361,6 +363,8 @@
HalDisplayId,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) override;
+ status_t executeCommands(HalDisplayId) override;
+
// set power mode
status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
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/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 6d4b0b5..a4ffd51 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -256,6 +256,9 @@
}
void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+ if (snapshot.isVisible != visible) {
+ snapshot.changes |= RequestedLayerState::Changes::Visibility;
+ }
snapshot.isVisible = visible;
// TODO(b/238781169) we are ignoring this compat for now, since we will have
@@ -314,6 +317,21 @@
}
}
+void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+ const LayerSnapshotBuilder::Args& args,
+ const LayerSnapshot& parentSnapshot) {
+ if (snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
+ snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
+ ? requested.gameMode
+ : parentSnapshot.gameMode;
+ }
+ updateMetadata(snapshot, requested, args);
+ if (args.includeMetadata) {
+ snapshot.layerMetadata = parentSnapshot.layerMetadata;
+ snapshot.layerMetadata.merge(requested.metadata);
+ }
+}
+
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
snapshot.clientChanges = 0;
@@ -759,6 +777,11 @@
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
+ if (forceUpdate ||
+ (args.includeMetadata &&
+ snapshot.changes.test(RequestedLayerState::Changes::Metadata))) {
+ updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
+ }
return;
}
@@ -798,15 +821,8 @@
}
}
- if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
- snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
- ? requested.gameMode
- : parentSnapshot.gameMode;
- updateMetadata(snapshot, requested, args);
- if (args.includeMetadata) {
- snapshot.layerMetadata = parentSnapshot.layerMetadata;
- snapshot.layerMetadata.merge(requested.metadata);
- }
+ if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::Metadata)) {
+ updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged ||
@@ -1175,6 +1191,15 @@
}
}
+void LayerSnapshotBuilder::forEachSnapshot(const Visitor& visitor,
+ const ConstPredicate& predicate) {
+ for (int i = 0; i < mNumInterestingSnapshots; i++) {
+ std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
+ if (!predicate(*snapshot)) continue;
+ visitor(snapshot);
+ }
+}
+
void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 1cec018..dbbad76 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -86,6 +86,11 @@
// Visit each visible snapshot in z-order and move the snapshot if needed
void forEachVisibleSnapshot(const Visitor& visitor);
+ typedef std::function<bool(const LayerSnapshot& snapshot)> ConstPredicate;
+ // Visit each snapshot that satisfies the predicate and move the snapshot if needed with visible
+ // snapshots in z-order
+ void forEachSnapshot(const Visitor& visitor, const ConstPredicate& predicate);
+
// Visit each snapshot interesting to input reverse z-order
void forEachInputSnapshot(const ConstVisitor& visitor) const;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 3e8d740..c3c2999 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -328,6 +328,7 @@
changes |= RequestedLayerState::Changes::GameMode;
}
}
+ changes |= RequestedLayerState::Changes::Metadata;
}
if (clientState.what & layer_state_t::eFrameRateChanged) {
const auto compatibility =
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6b97e2f..d27bfd2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3149,8 +3149,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
- const FrameTimelineInfo& info) {
+ bool isAutoTimestamp, const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
@@ -3224,10 +3223,11 @@
setFrameTimelineVsyncForBufferTransaction(info, postTime);
- if (dequeueTime && *dequeueTime != 0) {
+ if (bufferData.dequeueTime > 0) {
const uint64_t bufferId = mDrawingState.buffer->getId();
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber,
+ bufferData.dequeueTime,
FrameTracer::FrameEvent::DEQUEUE);
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
FrameTracer::FrameEvent::QUEUE);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9db7664..b9fcd5c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -312,7 +312,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+ const FrameTimelineInfo& /*info*/);
void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
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/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 2c66492..be5ffbc 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1066,6 +1066,11 @@
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
to_string(overrideFps).c_str(), uid);
+ if (ATRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
+ std::stringstream ss;
+ ss << "FrameRateOverride " << uid;
+ ATRACE_INT(ss.str().c_str(), overrideFps.getIntValue());
+ }
frameRateOverrides.emplace(uid, overrideFps);
}
@@ -1636,7 +1641,7 @@
case FrameRateCategory::Normal:
return FpsRange{60_Hz, 120_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 120_Hz};
+ return FpsRange{48_Hz, 120_Hz};
case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 60681a2..e669261 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -123,19 +123,22 @@
promotePacesetterDisplay(pacesetterIdOpt);
}
-void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ PhysicalDisplayId activeDisplayId) {
auto schedulePtr =
std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures,
[this](PhysicalDisplayId id, bool enable) {
onHardwareVsyncRequest(id, enable);
});
- registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
+ registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr),
+ activeDisplayId);
}
void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
RefreshRateSelectorPtr selectorPtr,
- VsyncSchedulePtr schedulePtr) {
+ VsyncSchedulePtr schedulePtr,
+ PhysicalDisplayId activeDisplayId) {
demotePacesetterDisplay();
auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
@@ -145,7 +148,7 @@
std::move(schedulePtr), mFeatures)
.second;
- return std::make_pair(promotePacesetterDisplayLocked(), isNew);
+ return std::make_pair(promotePacesetterDisplayLocked(activeDisplayId), isNew);
}();
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
@@ -158,7 +161,9 @@
dispatchHotplug(displayId, Hotplug::Connected);
}
-void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId, PhysicalDisplayId activeDisplayId) {
+ LOG_ALWAYS_FATAL_IF(displayId == activeDisplayId, "Cannot unregister the active display!");
+
dispatchHotplug(displayId, Hotplug::Disconnected);
demotePacesetterDisplay();
@@ -173,7 +178,7 @@
// headless virtual display.)
LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
- pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
+ pacesetterVsyncSchedule = promotePacesetterDisplayLocked(activeDisplayId);
}
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
@@ -1113,8 +1118,10 @@
.emitEvent = !choice.consideredSignals.idle});
}
- frameRateOverridesChanged = updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
-
+ if (!FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
+ frameRateOverridesChanged =
+ updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
+ }
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
refreshRateChanged = true;
@@ -1129,6 +1136,12 @@
if (refreshRateChanged) {
mSchedulerCallback.requestDisplayModes(std::move(modeRequests));
}
+
+ if (FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
+ std::scoped_lock lock(mPolicyLock);
+ frameRateOverridesChanged =
+ updateFrameRateOverridesLocked(consideredSignals, mPolicy.modeOpt->fps);
+ }
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ccaa05f..1a4aa79 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -101,9 +101,16 @@
using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>;
using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>;
- void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+ // After registration/unregistration, `activeDisplayId` is promoted to pacesetter. Note that the
+ // active display is never unregistered, since hotplug disconnect never happens for activatable
+ // displays, i.e. a foldable's internal displays or otherwise the (internal or external) primary
+ // display.
+ // TODO: b/255635821 - Remove active display parameters.
+ void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr,
+ PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
- void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId, PhysicalDisplayId activeDisplayId)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void run();
@@ -390,8 +397,9 @@
// the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
- void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr)
- REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr,
+ PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
struct Policy;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 85ce713..0644aca 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -360,7 +360,11 @@
purgeTimelines(now);
for (auto& timeline : mTimelines) {
- if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
+ const bool isVsyncValid = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? timeline.isWithin(TimePoint::fromNs(vsync)) ==
+ VsyncTimeline::VsyncOnTimeline::Unique
+ : timeline.validUntil() && timeline.validUntil()->ns() > vsync;
+ if (isVsyncValid) {
return timeline.isVSyncInPhase(model, vsync, frameRate);
}
}
@@ -395,8 +399,14 @@
mLastCommittedVsync = TimePoint::fromNs(0);
} else {
- mTimelines.back().freeze(
- TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ if (FlagManager::getInstance().vrr_bugfix_24q4()) {
+ // We need to freeze the timeline at the committed vsync so that we don't
+ // overshoot the deadline.
+ mTimelines.back().freeze(mLastCommittedVsync);
+ } else {
+ mTimelines.back().freeze(
+ TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ }
}
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
purgeTimelines(TimePoint::fromNs(mClock->now()));
@@ -611,7 +621,10 @@
while (mTimelines.size() > 1) {
const auto validUntilOpt = mTimelines.front().validUntil();
- if (validUntilOpt && *validUntilOpt < now) {
+ const bool isTimelineOutDated = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? mTimelines.front().isWithin(now) == VsyncTimeline::VsyncOnTimeline::Outside
+ : validUntilOpt && *validUntilOpt < now;
+ if (isTimelineOutDated) {
mTimelines.pop_front();
} else {
break;
@@ -660,9 +673,12 @@
vsyncTime += missedVsync.fixup.ns();
ATRACE_FORMAT_INSTANT("lastFrameMissed");
} else if (mightBackpressure && lastVsyncOpt) {
- // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
- // first before trying to use it.
- lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ if (!FlagManager::getInstance().vrr_bugfix_24q4()) {
+ // lastVsyncOpt does not need to be corrected with the new rate, and
+ // it should be used as is to avoid skipping a frame when changing rates are
+ // aligned at vsync time.
+ lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ }
const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
// avoid a duplicate vsync
@@ -681,7 +697,10 @@
}
ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
- if (mValidUntil && vsyncTime > mValidUntil->ns()) {
+ const bool isVsyncInvalid = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? isWithin(TimePoint::fromNs(vsyncTime)) == VsyncOnTimeline::Outside
+ : mValidUntil && vsyncTime > mValidUntil->ns();
+ if (isVsyncInvalid) {
ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
return std::nullopt;
@@ -737,7 +756,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/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 8ce61d8..66a7d71 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -106,6 +106,24 @@
void shiftVsyncSequence(Duration phase);
void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
+ enum class VsyncOnTimeline {
+ Unique, // Within timeline, not shared with next timeline.
+ Shared, // Within timeline, shared with next timeline.
+ Outside, // Outside of the timeline.
+ };
+ VsyncOnTimeline isWithin(TimePoint vsync) {
+ const auto threshold = mIdealPeriod.ns() / 2;
+ if (!mValidUntil || vsync.ns() < mValidUntil->ns() - threshold) {
+ // if mValidUntil is absent then timeline is not frozen and
+ // vsync should be unique to that timeline.
+ return VsyncOnTimeline::Unique;
+ }
+ if (vsync.ns() > mValidUntil->ns() + threshold) {
+ return VsyncOnTimeline::Outside;
+ }
+ return VsyncOnTimeline::Shared;
+ }
+
private:
nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5f81cd4..fd4bd11 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));
@@ -874,8 +891,12 @@
}
mCompositionEngine->setTimeStats(mTimeStats);
+
mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
- mCompositionEngine->getHwComposer().setCallback(*this);
+ auto& composer = mCompositionEngine->getHwComposer();
+ composer.setCallback(*this);
+ mDisplayModeController.setHwComposer(&composer);
+
ClientCache::getInstance().setRenderEngine(&getRenderEngine());
mHasReliablePresentFences =
@@ -914,6 +935,20 @@
// initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
initScheduler(display);
+ // Start listening after creating the Scheduler, since the listener calls into it.
+ mDisplayModeController.setActiveModeListener(
+ display::DisplayModeController::ActiveModeListener::make(
+ [this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderRate) {
+ // This callback cannot lock mStateLock, as some callers already lock it.
+ // Instead, switch context to the main thread.
+ static_cast<void>(mScheduler->schedule([=,
+ this]() FTL_FAKE_GUARD(mStateLock) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ display->updateRefreshRateOverlayRate(vsyncRate, renderRate);
+ }
+ }));
+ }));
+
mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
auto snapshot = perfetto::protos::LayersSnapshotProto{};
mScheduler
@@ -1279,19 +1314,19 @@
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto display = getDisplayDeviceLocked(displayId);
- if (!display) {
- ALOGW("%s: display is no longer valid", __func__);
- return;
- }
-
const bool emitEvent = desiredMode.emitEvent;
- switch (display->setDesiredMode(std::move(desiredMode))) {
- case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
- // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
- mScheduler->setRenderRate(displayId, display->refreshRateSelector().getActiveMode().fps,
- /*applyImmediately*/ true);
+ using DesiredModeAction = display::DisplayModeController::DesiredModeAction;
+
+ switch (mDisplayModeController.setDesiredMode(displayId, std::move(desiredMode))) {
+ case DesiredModeAction::InitiateDisplayModeSwitch: {
+ const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
+ if (!selectorPtr) break;
+
+ const Fps renderRate = selectorPtr->getActiveMode().fps;
+
+ // DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
+ mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
// Schedule a new frame to initiate the display mode switch.
scheduleComposite(FrameHint::kNone);
@@ -1311,7 +1346,8 @@
mScheduler->setModeChangePending(true);
break;
- case DisplayDevice::DesiredModeAction::InitiateRenderRateSwitch:
+ }
+ case DesiredModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false);
if (displayId == mActiveDisplayId) {
@@ -1322,7 +1358,7 @@
dispatchDisplayModeChangeEvent(displayId, mode);
}
break;
- case DisplayDevice::DesiredModeAction::None:
+ case DesiredModeAction::None:
break;
}
}
@@ -1378,11 +1414,10 @@
return future.get();
}
-void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) {
- const auto displayId = display.getPhysicalId();
+void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto pendingModeOpt = display.getPendingMode();
+ const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
@@ -1391,8 +1426,12 @@
const auto& activeMode = pendingModeOpt->mode;
- if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
+ if (const auto oldResolution =
+ mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
+ oldResolution != activeMode.modePtr->getResolution()) {
+ Mutex::Autolock lock(mStateLock);
+
+ auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
@@ -1403,8 +1442,8 @@
return;
}
- display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
- activeMode.fps);
+ mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
+ activeMode.modePtr->getVsyncRate(), activeMode.fps);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(activeMode.fps);
@@ -1415,21 +1454,20 @@
}
}
-void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) {
- display->clearDesiredMode();
- if (display->getPhysicalId() == mActiveDisplayId) {
+void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) {
+ mDisplayModeController.clearDesiredMode(displayId);
+ if (displayId == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
-void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) {
- const auto activeModeOpt = display->getDesiredMode();
+void SurfaceFlinger::applyActiveMode(PhysicalDisplayId displayId) {
+ const auto activeModeOpt = mDisplayModeController.getDesiredMode(displayId);
auto activeModePtr = activeModeOpt->mode.modePtr;
- const auto displayId = activeModePtr->getPhysicalDisplayId();
const auto renderFps = activeModeOpt->mode.fps;
- dropModeRequest(display);
+ dropModeRequest(displayId);
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
@@ -1445,11 +1483,8 @@
std::optional<PhysicalDisplayId> displayToUpdateImmediately;
- for (const auto& [id, physical] : mPhysicalDisplays) {
- const auto display = getDisplayDeviceLocked(id);
- if (!display) continue;
-
- auto desiredModeOpt = display->getDesiredMode();
+ for (const auto& [displayId, physical] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) {
+ auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
if (!desiredModeOpt) {
continue;
}
@@ -1466,19 +1501,21 @@
ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
ftl::to_underlying(desiredModeId),
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
- to_string(display->getId()).c_str());
+ to_string(displayId).c_str());
if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
- display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
+ mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ applyActiveMode(displayId);
continue;
}
+ const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
+
// Desired active mode was set, it is different than the mode currently in use, however
// allowed modes might have changed by the time we process the refresh.
// Make sure the desired mode is still allowed
- if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) {
- dropModeRequest(display);
+ if (!selectorPtr->isModeAllowed(desiredModeOpt->mode)) {
+ dropModeRequest(displayId);
continue;
}
@@ -1488,11 +1525,12 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) {
+ if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+ constraints, outTimeline)) {
continue;
}
- display->refreshRateSelector().onModeChangeInitiated();
+ selectorPtr->onModeChangeInitiated();
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
if (outTimeline.refreshRequired) {
@@ -1501,17 +1539,18 @@
// TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
// for all displays. This was only needed when the loop iterated over `mDisplays` rather
// than `mPhysicalDisplays`.
- displayToUpdateImmediately = display->getPhysicalId();
+ displayToUpdateImmediately = displayId;
}
}
if (displayToUpdateImmediately) {
- const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
- finalizeDisplayModeChange(*display);
+ const auto displayId = *displayToUpdateImmediately;
+ finalizeDisplayModeChange(displayId);
- const auto desiredModeOpt = display->getDesiredMode();
- if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
+ const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
+ if (desiredModeOpt &&
+ mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ applyActiveMode(displayId);
}
}
}
@@ -2259,8 +2298,10 @@
getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos
: data.vsyncPeriodNanos);
ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue());
- display->updateRefreshRateOverlayRate(refreshRate, display->getActiveMode().fps,
- /* showRefreshRate */ true);
+
+ const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps;
+ constexpr bool kSetByHwc = true;
+ display->updateRefreshRateOverlayRate(refreshRate, renderRate, kSetByHwc);
}
}
}));
@@ -2568,33 +2609,18 @@
// If a mode set is pending and the fence hasn't fired yet, wait for the next commit.
if (std::any_of(frameTargets.begin(), frameTargets.end(),
- [this](const auto& pair) FTL_FAKE_GUARD(mStateLock)
- FTL_FAKE_GUARD(kMainThreadContext) {
- if (!pair.second->isFramePending()) return false;
-
- if (const auto display = getDisplayDeviceLocked(pair.first)) {
- return display->isModeSetPending();
- }
-
- return false;
- })) {
+ [this](const auto& pair) FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto [displayId, target] = pair;
+ return target->isFramePending() &&
+ mDisplayModeController.isModeSetPending(displayId);
+ })) {
mScheduler->scheduleFrame();
return false;
}
- {
- Mutex::Autolock lock(mStateLock);
-
- for (const auto [id, target] : frameTargets) {
- // TODO(b/241285876): This is `nullptr` when the DisplayDevice is about to be removed in
- // this commit, since the PhysicalDisplay has already been removed. Rather than checking
- // for `nullptr` below, change Scheduler::onFrameSignal to filter out the FrameTarget of
- // the removed display.
- const auto display = getDisplayDeviceLocked(id);
-
- if (display && display->isModeSetPending()) {
- finalizeDisplayModeChange(*display);
- }
+ for (const auto [displayId, _] : frameTargets) {
+ if (mDisplayModeController.isModeSetPending(displayId)) {
+ finalizeDisplayModeChange(displayId);
}
}
@@ -2629,8 +2655,8 @@
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
- const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
- const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod();
+ const Period idealVsyncPeriod =
+ mDisplayModeController.getActiveMode(pacesetterId).fps.getPeriod();
mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
}
@@ -2693,9 +2719,10 @@
? &mLayerHierarchyBuilder.getHierarchy()
: nullptr,
updateAttachedChoreographer);
- initiateDisplayModeChanges();
}
+ initiateDisplayModeChanges();
+
updateCursorAsync();
if (!mustComposite) {
updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
@@ -3741,7 +3768,8 @@
if (const auto& physical = state.physical) {
const auto& mode = *physical->activeMode;
- display->setActiveMode(mode.getId(), mode.getVsyncRate(), mode.getVsyncRate());
+ mDisplayModeController.setActiveMode(physical->id, mode.getId(), mode.getVsyncRate(),
+ mode.getVsyncRate());
}
display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -3827,7 +3855,8 @@
ftl::FakeGuard guard(kMainThreadContext);
// For hotplug reconnect, renew the registration since display modes have been reloaded.
- mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(),
+ mActiveDisplayId);
}
if (display->isVirtual()) {
@@ -3866,7 +3895,7 @@
if (display->isVirtual()) {
releaseVirtualDisplay(display->getVirtualId());
} else {
- mScheduler->unregisterDisplay(display->getPhysicalId());
+ mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
}
}
@@ -3920,7 +3949,9 @@
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- mScheduler->resetPhaseConfiguration(display->getActiveMode().fps);
+ const Fps refreshRate =
+ mDisplayModeController.getActiveMode(display->getPhysicalId()).fps;
+ mScheduler->resetPhaseConfiguration(refreshRate);
}
}
return;
@@ -4476,7 +4507,8 @@
getFactory(), activeRefreshRate, *mTimeStats);
// The pacesetter must be registered before EventThread creation below.
- mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(),
+ mActiveDisplayId);
if (FlagManager::getInstance().vrr_config()) {
mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps,
/*applyImmediately*/ true);
@@ -5137,7 +5169,7 @@
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
@@ -5152,7 +5184,7 @@
composerState.state.sanitize(permissions);
}
- for (DisplayState display : displays) {
+ for (DisplayState& display : displays) {
display.sanitize(permissions);
}
@@ -5618,10 +5650,7 @@
layer->setInputInfo(*s.windowInfoHandle->getInfo());
flags |= eTraversalNeeded;
}
- std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
- dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
-
if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
gameMode != -1) {
// The transaction will be received on the Task layer and needs to be applied to all
@@ -5763,8 +5792,7 @@
if (what & layer_state_t::eBufferChanged) {
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -5850,10 +5878,6 @@
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
}
- std::optional<nsecs_t> dequeueBufferTimestamp;
- if (what & layer_state_t::eMetadataChanged) {
- dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
- }
std::vector<sp<CallbackHandle>> callbackHandles;
if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
@@ -5900,8 +5924,7 @@
}
layer->setTransformHint(transformHint);
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
mLayersWithQueuedFrames.emplace(layer);
@@ -6368,15 +6391,23 @@
return NO_ERROR;
}
- // Traversal of drawing state must happen on the main thread.
- // Otherwise, SortedVector may have shared ownership during concurrent
- // traversals, which can result in use-after-frees.
+ // Collect debug data from main thread
std::string compositionLayers;
mScheduler
->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
dumpVisibleFrontEnd(compositionLayers);
})
.get();
+ // get window info listener data without the state lock
+ auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
+ compositionLayers.append("Window Infos:\n");
+ StringAppendF(&compositionLayers, " max send vsync id: %" PRId64 "\n",
+ ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
+ StringAppendF(&compositionLayers, " max send delay (ns): %" PRId64 " ns\n",
+ windowInfosDebug.maxSendDelayDuration);
+ StringAppendF(&compositionLayers, " unsent messages: %zu\n",
+ windowInfosDebug.pendingMessageCount);
+ compositionLayers.append("\n");
dumpAll(args, compositionLayers, result);
write(fd, result.c_str(), result.size());
return NO_ERROR;
@@ -6959,15 +6990,6 @@
result.append(mTimeStats->miniDump());
result.append("\n");
-
- result.append("Window Infos:\n");
- auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
- StringAppendF(&result, " max send vsync id: %" PRId64 "\n",
- ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
- StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n",
- windowInfosDebug.maxSendDelayDuration);
- StringAppendF(&result, " unsent messages: %zu\n", windowInfosDebug.pendingMessageCount);
- result.append("\n");
}
mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
@@ -7667,9 +7689,10 @@
if (!display->isRefreshRateOverlayEnabled()) return;
const auto desiredModeIdOpt =
- display->getDesiredMode().transform([](const display::DisplayModeRequest& request) {
- return request.mode.modePtr->getId();
- });
+ mDisplayModeController.getDesiredMode(display->getPhysicalId())
+ .transform([](const display::DisplayModeRequest& request) {
+ return request.mode.modePtr->getId();
+ });
const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -7846,14 +7869,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 +7955,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 +7999,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 +8016,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 +8098,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 +8118,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 +8133,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 +8149,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 +8184,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 +8378,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 +8411,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 +8441,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 +8557,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) {
@@ -8836,20 +8927,26 @@
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
- for (const auto& [id, display] : mPhysicalDisplays) {
- if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
+ for (const auto& [displayId, physical] : mPhysicalDisplays) {
+ if (physical.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
FlagManager::getInstance().refresh_rate_overlay_on_external_display()) {
- if (const auto device = getDisplayDeviceLocked(id)) {
- const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
- kMainThreadContext) {
- device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
- mRefreshRateOverlayRenderRate,
- mRefreshRateOverlayShowInMiddle);
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ const auto enableOverlay = [&](bool setByHwc) FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto activeMode = mDisplayModeController.getActiveMode(displayId);
+ const Fps refreshRate = activeMode.modePtr->getVsyncRate();
+ const Fps renderFps = activeMode.fps;
+
+ display->enableRefreshRateOverlay(enable, setByHwc, refreshRate, renderFps,
+ mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate,
+ mRefreshRateOverlayShowInMiddle);
};
+
enableOverlay(setByHwc);
if (setByHwc) {
const auto status =
- getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(displayId,
+ enable);
if (status != NO_ERROR) {
ALOGE("Error %s refresh rate changed callback debug",
enable ? "enabling" : "disabling");
@@ -9005,7 +9102,7 @@
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(mDisplayModeController.getActiveMode(mActiveDisplayId).fps);
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
@@ -9239,7 +9336,9 @@
std::vector<std::pair<Layer*, LayerFE*>> layers;
if (mLayerLifecycleManagerEnabled) {
nsecs_t currentTime = systemTime();
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ const bool needsMetadata = mCompositionEngine->getFeatureFlags().test(
+ compositionengine::Feature::kSnapshotLayerMetadata);
+ mLayerSnapshotBuilder.forEachSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
kMainThreadContext) {
if (cursorOnly &&
@@ -9262,6 +9361,12 @@
layerFE->mSnapshot = std::move(snapshot);
refreshArgs.layers.push_back(layerFE);
layers.emplace_back(legacyLayer.get(), layerFE.get());
+ },
+ [needsMetadata](const frontend::LayerSnapshot& snapshot) {
+ return snapshot.isVisible ||
+ (needsMetadata &&
+ snapshot.changes.test(
+ frontend::RequestedLayerState::Changes::Metadata));
});
}
if (!mLayerLifecycleManagerEnabled) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8a39016..7762bbe 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -559,7 +559,7 @@
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -737,12 +737,12 @@
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
Fps maxFps);
- void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
- void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
+ void initiateDisplayModeChanges() REQUIRES(kMainThreadContext) EXCLUDES(mStateLock);
+ void finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+ EXCLUDES(mStateLock);
- // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler.
- void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock);
- void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ void dropModeRequest(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+ void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext);
// Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
@@ -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
@@ -1084,8 +1098,7 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&)
- REQUIRES(mStateLock);
+ void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&);
/*
* VSYNC
@@ -1325,9 +1338,7 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
// The inner or outer display for foldables, assuming they have mutually exclusive power states.
- // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
- // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
- std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
+ std::atomic<PhysicalDisplayId> mActiveDisplayId;
display::DisplayModeController mDisplayModeController;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index fc496b2..0bafb71 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -436,6 +436,7 @@
layer.bufferData->flags = ftl::Flags<BufferData::BufferDataChange>(bufferProto.flags());
layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
layer.bufferData->acquireFence = Fence::NO_FENCE;
+ layer.bufferData->dequeueTime = -1;
}
if (proto.what() & layer_state_t::eApiChanged) {
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 6b971a7..bcf1886 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",
@@ -37,6 +38,7 @@
"libsurfaceflingerflags",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
@@ -49,6 +51,7 @@
"libsurfaceflingerflags_test",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
@@ -56,12 +59,14 @@
name: "libsurfaceflinger_common_deps",
shared_libs: [
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"libsurfaceflinger_common",
"libsurfaceflingerflags",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
@@ -69,11 +74,13 @@
name: "libsurfaceflinger_common_test_deps",
shared_libs: [
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 57b170f..a56bb51 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -27,6 +27,7 @@
#include <cinttypes>
#include <android_os.h>
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <com_android_server_display_feature_flags.h>
@@ -135,6 +136,7 @@
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
+ DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame);
DUMP_READ_ONLY_FLAG(restore_blur_step);
DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
DUMP_READ_ONLY_FLAG(protected_if_client);
@@ -148,6 +150,10 @@
DUMP_READ_ONLY_FLAG(commit_not_composited);
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);
+ DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
@@ -237,6 +243,7 @@
FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
+FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
@@ -246,6 +253,9 @@
FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
+FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
+FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
+FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
@@ -258,5 +268,7 @@
FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
com::android::server::display::feature::flags)
FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
+FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(trace_frame_rate_override, "",
+ com::android::graphics::libgui::flags);
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 9517ff7..8799295 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -73,6 +73,7 @@
bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
bool vrr_bugfix_24q4() const;
+ bool vrr_bugfix_dropped_frame() const;
bool renderable_buffer_usage() const;
bool restore_blur_step() const;
bool dont_skip_on_early_ro() const;
@@ -87,6 +88,10 @@
bool commit_not_composited() const;
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;
+ bool trace_frame_rate_override() 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 02d8819..919ec17 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -55,6 +55,25 @@
} # detached_mirror
flag {
+ name: "flush_buffer_slots_to_uncache"
+ namespace: "core_graphics"
+ description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers."
+ bug: "330806421"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # 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"
@@ -84,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"
@@ -106,4 +136,15 @@
}
} # vrr_bugfix_24q4
+flag {
+ name: "vrr_bugfix_dropped_frame"
+ namespace: "core_graphics"
+ description: "bug fix for VRR dropped frame"
+ bug: "343603085"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # vrr_bugfix_dropped_frame
+
# IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index ebe11fb..d355e72 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -26,6 +26,7 @@
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <utils/String8.h>
#include <functional>
@@ -276,7 +277,7 @@
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
- std::function<status_t()> condition = [=]() {
+ std::function<status_t()> condition = [=, this]() {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
captureArgs.sourceCrop = {0, 0, 1, 1};
@@ -396,6 +397,56 @@
}
}
+TEST_F(CredentialsTest, DisplayTransactionPermissionTest) {
+ const auto display = getFirstDisplayToken();
+
+ ui::DisplayState displayState;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ const ui::Rotation initialOrientation = displayState.orientation;
+
+ // Set display orientation from an untrusted process. This should fail silently.
+ {
+ UIDFaker f{AID_BIN};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did not change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+
+ // Set display orientation from a trusted process.
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation + ui::ROTATION_90, displayState.orientation);
+
+ // Reset orientation
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation, layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 5145e11..98d5754 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,9 +70,9 @@
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
"DisplayDevice_GetBestColorModeTest.cpp",
- "DisplayDevice_InitiateModeChange.cpp",
"DisplayDevice_SetDisplayBrightnessTest.cpp",
"DisplayDevice_SetProjectionTest.cpp",
+ "DisplayModeControllerTest.cpp",
"EventThreadTest.cpp",
"FlagManagerTest.cpp",
"FpsReporterTest.cpp",
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/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
deleted file mode 100644
index c463a92..0000000
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include "DisplayTransactionTestHelpers.h"
-#include "mock/MockFrameRateMode.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
- ASSERT_TRUE(requestOpt); \
- EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
- EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
-
-namespace android {
-namespace {
-
-using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using DisplayModeRequest = display::DisplayModeRequest;
-
-class InitiateModeChangeTest : public DisplayTransactionTest {
-public:
- using Action = DisplayDevice::DesiredModeAction;
- void SetUp() override {
- injectFakeBufferQueueFactory();
- injectFakeNativeWindowSurfaceFactory();
-
- PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
- PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
- PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
- PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
- PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
-
- mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- DisplayHotplugEvent::CONNECTED);
- mFlinger.configureAndCommit();
-
- mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60)
- .inject();
- }
-
-protected:
- sp<DisplayDevice> mDisplay;
-
- static constexpr DisplayModeId kModeId60{0};
- static constexpr DisplayModeId kModeId90{1};
- static constexpr DisplayModeId kModeId120{2};
-
- static inline const ftl::NonNull<DisplayModePtr> kMode60 =
- ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
- static inline const ftl::NonNull<DisplayModePtr> kMode90 =
- ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
- static inline const ftl::NonNull<DisplayModePtr> kMode120 =
- ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
-
- static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false};
- static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true};
- static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false};
- static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true};
-};
-
-TEST_F(InitiateModeChangeTest, setDesiredModeToActiveMode) {
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60)));
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, setDesiredMode) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, clearDesiredMode) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_TRUE(mDisplay->getDesiredMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- const hal::VsyncPeriodChangeConstraints constraints{
- .desiredTimeNanos = systemTime(),
- .seamlessRequired = false,
- };
- hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) {
- EXPECT_EQ(Action::InitiateRenderRateSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode30)));
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- const hal::VsyncPeriodChangeConstraints constraints{
- .desiredTimeNanos = systemTime(),
- .seamlessRequired = false,
- };
- hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
-
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
new file mode 100644
index 0000000..d971150
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "Display/DisplayModeController.h"
+#include "Display/DisplaySnapshot.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayIdentificationTestHelpers.h"
+#include "FpsOps.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
+ ASSERT_TRUE(requestOpt); \
+ EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
+ EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
+
+namespace android::display {
+namespace {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+class DisplayModeControllerTest : public testing::Test {
+public:
+ using Action = DisplayModeController::DesiredModeAction;
+
+ void SetUp() override {
+ mDmc.setHwComposer(mComposer.get());
+ mDmc.setActiveModeListener(
+ [this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderFps) {
+ mActiveModeListener.Call(displayId, vsyncRate, renderFps);
+ });
+
+ constexpr uint8_t kPort = 111;
+ EXPECT_CALL(*mComposerHal, getDisplayIdentificationData(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kPort), SetArgPointee<2>(getInternalEdid()),
+ Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*mComposerHal, setClientTargetSlotCount(kHwcDisplayId));
+ EXPECT_CALL(*mComposerHal,
+ setVsyncEnabled(kHwcDisplayId, hal::IComposerClient::Vsync::DISABLE));
+ EXPECT_CALL(*mComposerHal, onHotplugConnect(kHwcDisplayId));
+
+ const auto infoOpt = mComposer->onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(infoOpt);
+
+ mDisplayId = infoOpt->id;
+ mDisplaySnapshotOpt.emplace(mDisplayId, ui::DisplayConnectionType::Internal,
+ makeModes(kMode60, kMode90, kMode120), ui::ColorModes{},
+ std::nullopt);
+
+ ftl::FakeGuard guard(kMainThreadContext);
+ mDmc.registerDisplay(*mDisplaySnapshotOpt, kModeId60,
+ scheduler::RefreshRateSelector::Config{});
+ }
+
+protected:
+ hal::VsyncPeriodChangeConstraints expectModeSet(const DisplayModeRequest& request,
+ hal::VsyncPeriodChangeTimeline& timeline,
+ bool subsequent = false) {
+ EXPECT_CALL(*mComposerHal,
+ isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+ .WillOnce(Return(true));
+
+ if (!subsequent) {
+ EXPECT_CALL(*mComposerHal, getDisplayConnectionType(kHwcDisplayId, _))
+ .WillOnce(DoAll(SetArgPointee<1>(
+ hal::IComposerClient::DisplayConnectionType::INTERNAL),
+ Return(hal::V2_4::Error::NONE)));
+ }
+
+ const hal::VsyncPeriodChangeConstraints constraints{
+ .desiredTimeNanos = systemTime(),
+ .seamlessRequired = false,
+ };
+
+ const hal::HWConfigId hwcModeId = request.mode.modePtr->getHwcId();
+
+ EXPECT_CALL(*mComposerHal,
+ setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+
+ return constraints;
+ }
+
+ static constexpr hal::HWDisplayId kHwcDisplayId = 1234;
+
+ Hwc2::mock::Composer* mComposerHal = new testing::StrictMock<Hwc2::mock::Composer>();
+ const std::unique_ptr<HWComposer> mComposer{
+ std::make_unique<impl::HWComposer>(std::unique_ptr<Hwc2::Composer>(mComposerHal))};
+
+ testing::MockFunction<void(PhysicalDisplayId, Fps, Fps)> mActiveModeListener;
+
+ DisplayModeController mDmc;
+
+ PhysicalDisplayId mDisplayId;
+ std::optional<DisplaySnapshot> mDisplaySnapshotOpt;
+
+ static constexpr DisplayModeId kModeId60{0};
+ static constexpr DisplayModeId kModeId90{1};
+ static constexpr DisplayModeId kModeId120{2};
+
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId120, 120_Hz));
+
+ static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true};
+ static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true};
+};
+
+TEST_F(DisplayModeControllerTest, setDesiredModeToActiveMode) {
+ EXPECT_CALL(mActiveModeListener, Call(_, _, _)).Times(0);
+
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode60)));
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, setDesiredMode) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+
+ // No action since a mode switch has already been initiated.
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode120)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, clearDesiredMode) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_TRUE(mDmc.getDesiredMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateModeChange) REQUIRES(kMainThreadContext) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+ auto modeRequest = kDesiredMode90;
+
+ hal::VsyncPeriodChangeTimeline timeline;
+ const auto constraints = expectModeSet(modeRequest, timeline);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateRenderRateSwitch) {
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 30_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateRenderRateSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode30)));
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+ auto modeRequest = kDesiredMode90;
+
+ hal::VsyncPeriodChangeTimeline timeline;
+ auto constraints = expectModeSet(modeRequest, timeline);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+
+ // No action since a mode switch has already been initiated.
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode120)));
+
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getDesiredMode(mDisplayId));
+ modeRequest = kDesiredMode120;
+
+ constexpr bool kSubsequent = true;
+ constraints = expectModeSet(modeRequest, timeline, kSubsequent);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+} // namespace
+} // namespace android::display
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/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 97bd79f..bc15dec 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -538,7 +538,8 @@
ftl::Flags<RequestedLayerState::Changes>(
RequestedLayerState::Changes::Content |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::VisibleRegion)
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Input)
.string());
EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(startingAlpha));
mLifecycleManager.commitChanges();
@@ -551,7 +552,8 @@
ftl::Flags<RequestedLayerState::Changes>(
RequestedLayerState::Changes::Content |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::VisibleRegion)
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Input)
.string());
EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(endingAlpha));
mLifecycleManager.commitChanges();
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index bd0accd..54d4659 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -213,6 +213,17 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
}
+TEST_F(LayerSnapshotTest, offscreenLayerSnapshotIsInvisible) {
+ EXPECT_EQ(getSnapshot(111)->isVisible, true);
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+ EXPECT_EQ(getSnapshot(111)->isVisible, false);
+ EXPECT_TRUE(getSnapshot(111)->changes.test(RequestedLayerState::Changes::Visibility));
+}
+
// relative tests
TEST_F(LayerSnapshotTest, RelativeParentCanHideChild) {
reparentRelativeLayer(13, 11);
@@ -280,13 +291,106 @@
transactions.back().states.front().layerId = 1;
transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
mLifecycleManager.applyTransactions(transactions);
- EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
}
+TEST_F(LayerSnapshotTest, UpdateMetadata) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly,
+ // and not using stale data.
+ transactions.back().states.front().state.metadata = LayerMetadata();
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789);
+
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Metadata);
+
+ // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = true,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ update(mSnapshotBuilder, args);
+
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789);
+}
+
+TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) {
+ hideLayer(1);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly,
+ // and not using stale data.
+ transactions.back().states.front().state.metadata = LayerMetadata();
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789);
+
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::Metadata | RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::AffectsChildren);
+
+ // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = true,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ update(mSnapshotBuilder, args);
+
+ EXPECT_EQ(static_cast<int64_t>(getSnapshot(1)->clientChanges),
+ layer_state_t::eMetadataChanged | layer_state_t::eFlagsChanged);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789);
+}
+
TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) {
// ROOT
// ├── 1
@@ -1161,6 +1265,16 @@
gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
}
+TEST_F(LayerSnapshotTest, alphaChangesPropagateToInput) {
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(1, touch);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ setAlpha(1, 0.5f);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->inputInfo.alpha, 0.5f);
+}
+
TEST_F(LayerSnapshotTest, isFrontBuffered) {
setBuffer(1,
std::make_shared<renderengine::mock::FakeExternalTexture>(
@@ -1283,6 +1397,17 @@
EXPECT_TRUE(foundInputLayer);
}
+TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) {
+ std::vector<uint32_t> visitedUniqueSequences;
+ mSnapshotBuilder.forEachSnapshot(
+ [&](const std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ visitedUniqueSequences.push_back(snapshot->uniqueSequence);
+ },
+ [](const frontend::LayerSnapshot& snapshot) { return snapshot.uniqueSequence == 111; });
+ EXPECT_EQ(visitedUniqueSequences.size(), 1u);
+ EXPECT_EQ(visitedUniqueSequences[0], 111u);
+}
+
TEST_F(LayerSnapshotTest, canOccludePresentation) {
setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation);
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index cf9a7d3..06c4e30 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1562,7 +1562,7 @@
// When frame rates get an equal score, the lower is chosen, unless there are Max votes.
{0_Hz, FrameRateCategory::High, 90_Hz},
{0_Hz, FrameRateCategory::Normal, 60_Hz},
- {0_Hz, FrameRateCategory::Low, 30_Hz},
+ {0_Hz, FrameRateCategory::Low, 60_Hz},
{0_Hz, FrameRateCategory::NoPreference, 60_Hz},
// Cases that have both desired frame rate and frame rate category requirements.
@@ -1609,6 +1609,77 @@
}
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_120_vrr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ // Device with VRR config mode
+ auto selector = createSelector(kVrrMode_120, kModeId120);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::initializer_list<Case> testCases = {
+ // Cases that only have frame rate category requirements, but no desired frame rate.
+ // When frame rates get an equal score, the lower is chosen, unless there are Max votes.
+ {0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::Low, 48_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 120_Hz},
+
+ // Cases that have both desired frame rate and frame rate category requirements.
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal, 60_Hz},
+ {24_Hz, FrameRateCategory::Low, 48_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 30_Hz},
+
+ // Cases that only have desired frame rate.
+ {30_Hz, FrameRateCategory::Default, 30_Hz},
+ };
+
+ for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers).fps)
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+}
+
TEST_P(RefreshRateSelectorTest,
getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) {
auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
@@ -2140,14 +2211,14 @@
// These layers may switch modes because smoothSwitchOnly=false.
{FrameRateCategory::Default, false, 120_Hz, kModeId120},
{FrameRateCategory::NoPreference, false, 120_Hz, kModeId120},
- {FrameRateCategory::Low, false, 30_Hz, kModeId60},
+ {FrameRateCategory::Low, false, 60_Hz, kModeId60},
{FrameRateCategory::Normal, false, 60_Hz, kModeId60},
{FrameRateCategory::High, false, 120_Hz, kModeId120},
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 120_Hz, kModeId120},
{FrameRateCategory::Normal, true, 120_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
@@ -2207,13 +2278,13 @@
{FrameRateCategory::Default, false, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
{FrameRateCategory::NoPreference, false, 120_Hz},
- {FrameRateCategory::Low, false, 30_Hz},
+ {FrameRateCategory::Low, false, 48_Hz},
{FrameRateCategory::Normal, false, 60_Hz},
{FrameRateCategory::High, false, 120_Hz},
{FrameRateCategory::Default, true, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
{FrameRateCategory::NoPreference, true, 120_Hz},
- {FrameRateCategory::Low, true, 30_Hz},
+ {FrameRateCategory::Low, true, 48_Hz},
{FrameRateCategory::Normal, true, 60_Hz},
{FrameRateCategory::High, true, 120_Hz},
};
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 4fb0690..fc54a8b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -343,12 +343,15 @@
}
TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
+ constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
- kDisplay1Mode60->getId()));
+ kDisplay1Mode60->getId()),
+ kActiveDisplayId);
mScheduler->registerDisplay(kDisplayId2,
std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
+ kDisplay2Mode60->getId()),
+ kActiveDisplayId);
mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);
@@ -411,10 +414,10 @@
{
// The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
// Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
- mScheduler
- ->registerDisplay(kDisplayId3,
- std::make_shared<RefreshRateSelector>(kDisplay3Modes,
- kDisplay3Mode60->getId()));
+ mScheduler->registerDisplay(kDisplayId3,
+ std::make_shared<RefreshRateSelector>(kDisplay3Modes,
+ kDisplay3Mode60->getId()),
+ kActiveDisplayId);
mScheduler->setDisplayPowerMode(kDisplayId3, hal::PowerMode::ON);
const GlobalSignals globalSignals = {.touch = true};
@@ -457,12 +460,15 @@
}
TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
+ constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
- kDisplay1Mode60->getId()));
+ kDisplay1Mode60->getId()),
+ kActiveDisplayId);
mScheduler->registerDisplay(kDisplayId2,
std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
+ kDisplay2Mode60->getId()),
+ kActiveDisplayId);
using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
@@ -585,7 +591,8 @@
mFlinger.getTimeStats(),
mSchedulerCallback};
- scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker);
+ scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, std::nullopt,
+ vrrTracker);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false);
vrrTracker->addVsyncTimestamp(0);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 078b4fe..0c3e875 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -76,6 +76,7 @@
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
.setRefreshRateSelector(std::move(selectorPtr))
.inject(std::move(vsyncController), std::move(vsyncTracker));
+ mDisplayId = mDisplay->getPhysicalId();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
// will call setActiveConfig instead of setActiveConfigWithConstraints.
@@ -112,7 +113,11 @@
protected:
void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
+ auto& dmc() { return mFlinger.mutableDisplayModeController(); }
+
sp<DisplayDevice> mDisplay, mOuterDisplay;
+ PhysicalDisplayId mDisplayId;
+
mock::EventThread* mAppEventThread;
static constexpr DisplayModeId kModeId60{0};
@@ -167,17 +172,17 @@
TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -187,8 +192,8 @@
Mock::VerifyAndClearExpectations(mComposer);
- EXPECT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
@@ -198,23 +203,23 @@
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90, true, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -226,8 +231,8 @@
mFlinger.commit();
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -236,8 +241,8 @@
// Test that if we call setDesiredDisplayModeSpecs while a previous mode change
// is still being processed the later call will be respected.
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -252,46 +257,45 @@
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId120, false, 0, 180));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120);
mFlinger.commit();
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
mFlinger.commit();
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId120);
}
TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90_4K);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K);
- EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
+ EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplayId, true));
- // Misc expecations. We don't need to enforce these method calls, but since the helper methods
- // already set expectations we should add new ones here, otherwise the test will fail.
+ // Override expectations set up by PrimaryDisplayVariant.
EXPECT_CALL(*mConsumer,
setDefaultBufferSize(static_cast<uint32_t>(kResolution4K.getWidth()),
static_cast<uint32_t>(kResolution4K.getHeight())))
@@ -304,31 +308,28 @@
injectFakeNativeWindowSurfaceFactory();
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
- const auto displayToken = mDisplay->getDisplayToken().promote();
-
mFlinger.commit();
- // The DisplayDevice will be destroyed and recreated,
- // so we need to update with the new instance.
- mDisplay = mFlinger.getDisplay(displayToken);
-
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90_4K);
}
MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
- if (!arg->getDesiredMode()) {
+ const auto displayId = arg->getPhysicalId();
+ auto& dmc = flinger->mutableDisplayModeController();
+
+ if (!dmc.getDesiredMode(displayId)) {
*result_listener << "No desired mode";
return false;
}
- if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) {
+ if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) {
*result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId);
return false;
}
// VsyncModulator should react to mode switches on the pacesetter display.
- if (arg->getPhysicalId() == flinger->scheduler()->pacesetterDisplayId() &&
+ if (displayId == flinger->scheduler()->pacesetterDisplayId() &&
!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
*result_listener << "VsyncModulator did not shift to early phase";
return false;
@@ -337,8 +338,10 @@
return true;
}
-MATCHER_P(ModeSettledTo, modeId, "") {
- if (const auto desiredOpt = arg->getDesiredMode()) {
+MATCHER_P2(ModeSettledTo, dmc, modeId, "") {
+ const auto displayId = arg->getPhysicalId();
+
+ if (const auto desiredOpt = dmc->getDesiredMode(displayId)) {
*result_listener << "Unsettled desired mode "
<< ftl::to_underlying(desiredOpt->mode.modePtr->getId());
return false;
@@ -346,7 +349,7 @@
ftl::FakeGuard guard(kMainThreadContext);
- if (arg->getActiveMode().modePtr->getId() != modeId) {
+ if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) {
*result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId);
return false;
}
@@ -367,14 +370,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -400,14 +403,14 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -420,12 +423,12 @@
mFlinger.commit();
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
@@ -440,14 +443,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -473,13 +476,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
EXPECT_TRUE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
@@ -502,7 +505,7 @@
mFlinger.commit();
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
@@ -518,14 +521,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -555,8 +558,8 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
@@ -570,13 +573,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId120));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 1bae5ff..933d03d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -232,7 +232,9 @@
// Invocation
DisplayDeviceState state;
- if constexpr (constexpr auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+
+ constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value;
+ if constexpr (kConnectionTypeOpt) {
const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
@@ -257,7 +259,7 @@
const auto it = mFlinger.mutablePhysicalDisplays()
.emplace_or_replace(*displayId, displayToken, *displayId,
- *connectionType, makeModes(activeMode),
+ *kConnectionTypeOpt, makeModes(activeMode),
std::move(colorModes), std::nullopt)
.first;
@@ -291,9 +293,12 @@
EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput,
device->receivesInput());
- if constexpr (Case::Display::CONNECTION_TYPE::value) {
+ if constexpr (kConnectionTypeOpt) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
+ EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID,
+ mFlinger.mutableDisplayModeController()
+ .getActiveMode(device->getPhysicalId())
+ .modePtr->getHwcId());
}
}
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 198a5de..f063809 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -53,7 +53,7 @@
factory, selectorPtr->getActiveMode().fps, timeStats) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
- std::move(tracker));
+ std::move(tracker), displayId);
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
// Execute task to prevent broken promise exception on destruction.
@@ -85,14 +85,16 @@
void registerDisplay(
PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ std::optional<PhysicalDisplayId> activeDisplayIdOpt = {},
std::shared_ptr<VSyncTracker> vsyncTracker = std::make_shared<mock::VSyncTracker>()) {
registerDisplay(displayId, std::move(selectorPtr),
- std::make_unique<mock::VsyncController>(), vsyncTracker);
+ std::make_unique<mock::VsyncController>(), vsyncTracker,
+ activeDisplayIdOpt.value_or(displayId));
}
void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
std::unique_ptr<VsyncController> controller,
- std::shared_ptr<VSyncTracker> tracker) {
+ std::shared_ptr<VSyncTracker> tracker, PhysicalDisplayId activeDisplayId) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr),
std::shared_ptr<VsyncSchedule>(
@@ -101,16 +103,12 @@
mock::VSyncDispatch>(),
std::move(controller),
mockRequestHardwareVsync
- .AsStdFunction())));
+ .AsStdFunction())),
+ activeDisplayId);
}
testing::MockFunction<void(PhysicalDisplayId, bool)> mockRequestHardwareVsync;
- void unregisterDisplay(PhysicalDisplayId displayId) {
- ftl::FakeGuard guard(kMainThreadContext);
- Scheduler::unregisterDisplay(displayId);
- }
-
void setDisplayPowerMode(PhysicalDisplayId displayId, hal::PowerMode powerMode) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::setDisplayPowerMode(displayId, powerMode);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 265f804..b5b36be 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -191,6 +191,8 @@
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
mFlinger->mCompositionEngine->setHwComposer(
std::make_unique<impl::HWComposer>(std::move(composer)));
+ mFlinger->mDisplayModeController.setHwComposer(
+ &mFlinger->mCompositionEngine->getHwComposer());
}
void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
@@ -485,23 +487,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,
@@ -521,7 +528,7 @@
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -1050,7 +1057,6 @@
auto& modes = mDisplayModes;
auto& activeModeId = mActiveModeId;
- std::optional<Fps> refreshRateOpt;
DisplayDeviceState state;
state.isSecure = mCreationArgs.isSecure;
@@ -1088,7 +1094,9 @@
const auto activeModeOpt = modes.get(activeModeId);
LOG_ALWAYS_FATAL_IF(!activeModeOpt);
- refreshRateOpt = activeModeOpt->get()->getPeakFps();
+
+ // Save a copy for use after `modes` is consumed.
+ const Fps refreshRate = activeModeOpt->get()->getPeakFps();
state.physical = {.id = *physicalId,
.hwcDisplayId = *mHwcDisplayId,
@@ -1104,21 +1112,20 @@
.registerDisplay(*physicalId, it->second.snapshot(),
mCreationArgs.refreshRateSelector);
+ mFlinger.mutableDisplayModeController().setActiveMode(*physicalId, activeModeId,
+ refreshRate, refreshRate);
+
if (mFlinger.scheduler() && mSchedulerRegistration) {
mFlinger.scheduler()->registerDisplay(*physicalId,
mCreationArgs.refreshRateSelector,
- std::move(controller),
- std::move(tracker));
+ std::move(controller), std::move(tracker),
+ mFlinger.mutableActiveDisplayId());
}
}
sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
- if (refreshRateOpt) {
- display->setActiveMode(activeModeId, *refreshRateOpt, *refreshRateOpt);
- }
-
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index d4d5b32..85b61f8 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -94,7 +94,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- dequeueTime, FrameTimelineInfo{});
+ FrameTimelineInfo{});
commitTransaction(layer.get());
nsecs_t latchTime = 25;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 5046a86..46733b9 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -99,7 +99,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -134,7 +134,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -151,7 +151,7 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -197,7 +197,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -232,7 +232,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -275,7 +275,7 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -320,7 +320,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -335,7 +335,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -372,7 +372,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -392,7 +392,7 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -413,7 +413,7 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -462,7 +462,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index eafba0a..f36a8a6 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++) {
@@ -651,6 +673,36 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
+TEST_F(VSyncPredictorTest, setRenderRateWhenRenderRateGoesDown) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ Fps frameRate = Fps::fromPeriodNsecs(1000);
+ vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ frameRate = Fps::fromPeriodNsecs(3000);
+ vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
+ EXPECT_TRUE(vrrTracker.isVSyncInPhase(2000, frameRate));
+}
+
TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index 2bf905c..0da9cc0 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -26,6 +26,8 @@
cc_library_headers {
name: "hwvulkan_headers",
+ host_supported: true, // Used for verification in Berberis.
+ native_bridge_supported: true, // Used for verification in Berberis.
vendor_available: true,
header_libs: [
"libcutils_headers",
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index a9706bc..a3fe33e 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -178,7 +178,7 @@
INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
- INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, instance, GetPhysicalDevicePresentRectanglesKHR);
INIT_PROC(false, instance, GetPhysicalDeviceToolProperties);
// clang-format on
@@ -325,9 +325,9 @@
INIT_PROC(false, dev, BindBufferMemory2);
INIT_PROC(false, dev, BindImageMemory2);
INIT_PROC(false, dev, CmdSetDeviceMask);
- INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR);
- INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR);
- INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupPresentCapabilitiesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupSurfacePresentModesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImage2KHR);
INIT_PROC(false, dev, CmdDispatchBase);
INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
@@ -659,6 +659,8 @@
"vkGetDrmDisplayEXT",
"vkGetInstanceProcAddr",
"vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
+ "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR",
+ "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR",
"vkGetPhysicalDeviceDisplayPlaneProperties2KHR",
"vkGetPhysicalDeviceDisplayProperties2KHR",
"vkGetPhysicalDeviceExternalBufferProperties",
@@ -703,6 +705,7 @@
"vkGetPhysicalDeviceToolProperties",
"vkGetPhysicalDeviceToolPropertiesEXT",
"vkGetPhysicalDeviceVideoCapabilitiesKHR",
+ "vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR",
"vkGetPhysicalDeviceVideoFormatPropertiesKHR",
"vkSubmitDebugUtilsMessageEXT",
};
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 74d3d9d..00e987f 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) {
@@ -1472,23 +1473,14 @@
image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
image_format_properties.pNext = &ahb_usage;
- if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
- VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
- pdev, &image_format_info, &image_format_properties);
- if (result != VK_SUCCESS) {
- ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- }
- else {
- VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
- pdev, &image_format_info,
- &image_format_properties);
- if (result != VK_SUCCESS) {
- ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
- result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
+ VkResult result = GetPhysicalDeviceImageFormatProperties2(
+ pdev, &image_format_info, &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE(
+ "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage "
+ "failed: %d",
+ result);
+ return VK_ERROR_SURFACE_LOST_KHR;
}
// Determine if USAGE_FRONT_BUFFER is needed.
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 866c1b7..6b4cbad 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -351,9 +351,50 @@
'external', 'vulkan-headers', 'registry', 'vk.xml')
tree = element_tree.parse(registry)
root = tree.getroot()
+
+ for exts in root.iter('extensions'):
+ for extension in exts.iter('extension'):
+ if 'vulkan' not in extension.get('supported').split(','):
+ # ANDROID_native_buffer is a weird special case -- it's declared in vk.xml as
+ # disabled but we _do_ want to generate plumbing for it in the Android loader.
+ if extension.get('name') != 'VK_ANDROID_native_buffer':
+ print('skip extension disabled or not for vulkan: ' + extension.get('name'))
+ continue
+
+ apiversion = 'VK_VERSION_1_0'
+ if extension.tag == 'extension':
+ extname = extension.get('name')
+ if (extension.get('type') == 'instance' and
+ extension.get('promotedto') is not None):
+ promoted_inst_ext_dict[extname] = \
+ version_2_api_version(extension.get('promotedto'))
+ for req in extension.iter('require'):
+ if req.get('feature') is not None:
+ apiversion = req.get('feature')
+ for commands in req:
+ if commands.tag == 'command':
+ cmd_name = commands.get('name')
+ if cmd_name not in extension_dict:
+ extension_dict[cmd_name] = extname
+ version_dict[cmd_name] = apiversion
+
+ for feature in root.iter('feature'):
+ if 'vulkan' not in feature.get('api').split(','):
+ continue
+
+ apiversion = feature.get('name')
+ for req in feature.iter('require'):
+ for command in req:
+ if command.tag == 'command':
+ cmd_name = command.get('name')
+ version_dict[cmd_name] = apiversion
+
for commands in root.iter('commands'):
for command in commands:
if command.tag == 'command':
+ if command.get('api') == 'vulkansc':
+ continue
+
parameter_list = []
protoset = False
cmd_name = ''
@@ -361,12 +402,18 @@
if command.get('alias') is not None:
alias = command.get('alias')
cmd_name = command.get('name')
- alias_dict[cmd_name] = alias
- command_list.append(cmd_name)
- param_dict[cmd_name] = param_dict[alias].copy()
- return_type_dict[cmd_name] = return_type_dict[alias]
+ # At this stage all valid commands have been added to the version
+ # dict so we can use it to filter valid commands
+ if cmd_name in version_dict:
+ alias_dict[cmd_name] = alias
+ command_list.append(cmd_name)
+ param_dict[cmd_name] = param_dict[alias].copy()
+ return_type_dict[cmd_name] = return_type_dict[alias]
for params in command:
if params.tag == 'param':
+ if params.get('api') == 'vulkansc':
+ # skip SC-only param variant
+ continue
param_type = ''
if params.text is not None and params.text.strip():
param_type = params.text.strip() + ' '
@@ -387,39 +434,13 @@
cmd_type = c.text
if c.tag == 'name':
cmd_name = c.text
- protoset = True
- command_list.append(cmd_name)
- return_type_dict[cmd_name] = cmd_type
+ if cmd_name in version_dict:
+ protoset = True
+ command_list.append(cmd_name)
+ return_type_dict[cmd_name] = cmd_type
if protoset:
param_dict[cmd_name] = parameter_list.copy()
- for exts in root.iter('extensions'):
- for extension in exts:
- apiversion = 'VK_VERSION_1_0'
- if extension.tag == 'extension':
- extname = extension.get('name')
- if (extension.get('type') == 'instance' and
- extension.get('promotedto') is not None):
- promoted_inst_ext_dict[extname] = \
- version_2_api_version(extension.get('promotedto'))
- for req in extension:
- if req.get('feature') is not None:
- apiversion = req.get('feature')
- for commands in req:
- if commands.tag == 'command':
- cmd_name = commands.get('name')
- if cmd_name not in extension_dict:
- extension_dict[cmd_name] = extname
- version_dict[cmd_name] = apiversion
-
- for feature in root.iter('feature'):
- apiversion = feature.get('name')
- for req in feature:
- for command in req:
- if command.tag == 'command':
- cmd_name = command.get('name')
- if cmd_name in command_list:
- version_dict[cmd_name] = apiversion
version_code_set = set()
for version in version_dict.values():
diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py
index e9faef6..3624c1d 100644
--- a/vulkan/scripts/null_generator.py
+++ b/vulkan/scripts/null_generator.py
@@ -89,6 +89,8 @@
f.write(gencom.copyright_and_warning(2015))
f.write("""\
+#include <android/hardware_buffer.h>
+
#include <algorithm>
#include "null_driver_gen.h"